roojs-debug.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours
1010   T      CST        Timezone setting of the machine running the code
1011   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1012 </pre>
1013  *
1014  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1015  * <pre><code>
1016 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1017 document.write(dt.format('Y-m-d'));                         //2007-01-10
1018 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1019 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
1020  </code></pre>
1021  *
1022  * Here are some standard date/time patterns that you might find helpful.  They
1023  * are not part of the source of Date.js, but to use them you can simply copy this
1024  * block of code into any script that is included after Date.js and they will also become
1025  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1026  * <pre><code>
1027 Date.patterns = {
1028     ISO8601Long:"Y-m-d H:i:s",
1029     ISO8601Short:"Y-m-d",
1030     ShortDate: "n/j/Y",
1031     LongDate: "l, F d, Y",
1032     FullDateTime: "l, F d, Y g:i:s A",
1033     MonthDay: "F d",
1034     ShortTime: "g:i A",
1035     LongTime: "g:i:s A",
1036     SortableDateTime: "Y-m-d\\TH:i:s",
1037     UniversalSortableDateTime: "Y-m-d H:i:sO",
1038     YearMonth: "F, Y"
1039 };
1040 </code></pre>
1041  *
1042  * Example usage:
1043  * <pre><code>
1044 var dt = new Date();
1045 document.write(dt.format(Date.patterns.ShortDate));
1046  </code></pre>
1047  */
1048
1049 /*
1050  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1051  * They generate precompiled functions from date formats instead of parsing and
1052  * processing the pattern every time you format a date.  These functions are available
1053  * on every Date object (any javascript function).
1054  *
1055  * The original article and download are here:
1056  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1057  *
1058  */
1059  
1060  
1061  // was in core
1062 /**
1063  Returns the number of milliseconds between this date and date
1064  @param {Date} date (optional) Defaults to now
1065  @return {Number} The diff in milliseconds
1066  @member Date getElapsed
1067  */
1068 Date.prototype.getElapsed = function(date) {
1069         return Math.abs((date || new Date()).getTime()-this.getTime());
1070 };
1071 // was in date file..
1072
1073
1074 // private
1075 Date.parseFunctions = {count:0};
1076 // private
1077 Date.parseRegexes = [];
1078 // private
1079 Date.formatFunctions = {count:0};
1080
1081 // private
1082 Date.prototype.dateFormat = function(format) {
1083     if (Date.formatFunctions[format] == null) {
1084         Date.createNewFormat(format);
1085     }
1086     var func = Date.formatFunctions[format];
1087     return this[func]();
1088 };
1089
1090
1091 /**
1092  * Formats a date given the supplied format string
1093  * @param {String} format The format string
1094  * @return {String} The formatted date
1095  * @method
1096  */
1097 Date.prototype.format = Date.prototype.dateFormat;
1098
1099 // private
1100 Date.createNewFormat = function(format) {
1101     var funcName = "format" + Date.formatFunctions.count++;
1102     Date.formatFunctions[format] = funcName;
1103     var code = "Date.prototype." + funcName + " = function(){return ";
1104     var special = false;
1105     var ch = '';
1106     for (var i = 0; i < format.length; ++i) {
1107         ch = format.charAt(i);
1108         if (!special && ch == "\\") {
1109             special = true;
1110         }
1111         else if (special) {
1112             special = false;
1113             code += "'" + String.escape(ch) + "' + ";
1114         }
1115         else {
1116             code += Date.getFormatCode(ch);
1117         }
1118     }
1119     /** eval:var:zzzzzzzzzzzzz */
1120     eval(code.substring(0, code.length - 3) + ";}");
1121 };
1122
1123 // private
1124 Date.getFormatCode = function(character) {
1125     switch (character) {
1126     case "d":
1127         return "String.leftPad(this.getDate(), 2, '0') + ";
1128     case "D":
1129         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1130     case "j":
1131         return "this.getDate() + ";
1132     case "l":
1133         return "Date.dayNames[this.getDay()] + ";
1134     case "S":
1135         return "this.getSuffix() + ";
1136     case "w":
1137         return "this.getDay() + ";
1138     case "z":
1139         return "this.getDayOfYear() + ";
1140     case "W":
1141         return "this.getWeekOfYear() + ";
1142     case "F":
1143         return "Date.monthNames[this.getMonth()] + ";
1144     case "m":
1145         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1146     case "M":
1147         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1148     case "n":
1149         return "(this.getMonth() + 1) + ";
1150     case "t":
1151         return "this.getDaysInMonth() + ";
1152     case "L":
1153         return "(this.isLeapYear() ? 1 : 0) + ";
1154     case "Y":
1155         return "this.getFullYear() + ";
1156     case "y":
1157         return "('' + this.getFullYear()).substring(2, 4) + ";
1158     case "a":
1159         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1160     case "A":
1161         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1162     case "g":
1163         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1164     case "G":
1165         return "this.getHours() + ";
1166     case "h":
1167         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1168     case "H":
1169         return "String.leftPad(this.getHours(), 2, '0') + ";
1170     case "i":
1171         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1172     case "s":
1173         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1174     case "O":
1175         return "this.getGMTOffset() + ";
1176     case "T":
1177         return "this.getTimezone() + ";
1178     case "Z":
1179         return "(this.getTimezoneOffset() * -60) + ";
1180     default:
1181         return "'" + String.escape(character) + "' + ";
1182     }
1183 };
1184
1185 /**
1186  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1187  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1188  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1189  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1190  * string or the parse operation will fail.
1191  * Example Usage:
1192 <pre><code>
1193 //dt = Fri May 25 2007 (current date)
1194 var dt = new Date();
1195
1196 //dt = Thu May 25 2006 (today's month/day in 2006)
1197 dt = Date.parseDate("2006", "Y");
1198
1199 //dt = Sun Jan 15 2006 (all date parts specified)
1200 dt = Date.parseDate("2006-1-15", "Y-m-d");
1201
1202 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1203 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1204 </code></pre>
1205  * @param {String} input The unparsed date as a string
1206  * @param {String} format The format the date is in
1207  * @return {Date} The parsed date
1208  * @static
1209  */
1210 Date.parseDate = function(input, format) {
1211     if (Date.parseFunctions[format] == null) {
1212         Date.createParser(format);
1213     }
1214     var func = Date.parseFunctions[format];
1215     return Date[func](input);
1216 };
1217 /**
1218  * @private
1219  */
1220 Date.createParser = function(format) {
1221     var funcName = "parse" + Date.parseFunctions.count++;
1222     var regexNum = Date.parseRegexes.length;
1223     var currentGroup = 1;
1224     Date.parseFunctions[format] = funcName;
1225
1226     var code = "Date." + funcName + " = function(input){\n"
1227         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1228         + "var d = new Date();\n"
1229         + "y = d.getFullYear();\n"
1230         + "m = d.getMonth();\n"
1231         + "d = d.getDate();\n"
1232         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1233         + "if (results && results.length > 0) {";
1234     var regex = "";
1235
1236     var special = false;
1237     var ch = '';
1238     for (var i = 0; i < format.length; ++i) {
1239         ch = format.charAt(i);
1240         if (!special && ch == "\\") {
1241             special = true;
1242         }
1243         else if (special) {
1244             special = false;
1245             regex += String.escape(ch);
1246         }
1247         else {
1248             var obj = Date.formatCodeToRegex(ch, currentGroup);
1249             currentGroup += obj.g;
1250             regex += obj.s;
1251             if (obj.g && obj.c) {
1252                 code += obj.c;
1253             }
1254         }
1255     }
1256
1257     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1258         + "{v = new Date(y, m, d, h, i, s);}\n"
1259         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1260         + "{v = new Date(y, m, d, h, i);}\n"
1261         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1262         + "{v = new Date(y, m, d, h);}\n"
1263         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1264         + "{v = new Date(y, m, d);}\n"
1265         + "else if (y >= 0 && m >= 0)\n"
1266         + "{v = new Date(y, m);}\n"
1267         + "else if (y >= 0)\n"
1268         + "{v = new Date(y);}\n"
1269         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1270         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1271         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1272         + ";}";
1273
1274     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1275     /** eval:var:zzzzzzzzzzzzz */
1276     eval(code);
1277 };
1278
1279 // private
1280 Date.formatCodeToRegex = function(character, currentGroup) {
1281     switch (character) {
1282     case "D":
1283         return {g:0,
1284         c:null,
1285         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1286     case "j":
1287         return {g:1,
1288             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1289             s:"(\\d{1,2})"}; // day of month without leading zeroes
1290     case "d":
1291         return {g:1,
1292             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1293             s:"(\\d{2})"}; // day of month with leading zeroes
1294     case "l":
1295         return {g:0,
1296             c:null,
1297             s:"(?:" + Date.dayNames.join("|") + ")"};
1298     case "S":
1299         return {g:0,
1300             c:null,
1301             s:"(?:st|nd|rd|th)"};
1302     case "w":
1303         return {g:0,
1304             c:null,
1305             s:"\\d"};
1306     case "z":
1307         return {g:0,
1308             c:null,
1309             s:"(?:\\d{1,3})"};
1310     case "W":
1311         return {g:0,
1312             c:null,
1313             s:"(?:\\d{2})"};
1314     case "F":
1315         return {g:1,
1316             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1317             s:"(" + Date.monthNames.join("|") + ")"};
1318     case "M":
1319         return {g:1,
1320             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1321             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1322     case "n":
1323         return {g:1,
1324             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1325             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1326     case "m":
1327         return {g:1,
1328             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1329             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1330     case "t":
1331         return {g:0,
1332             c:null,
1333             s:"\\d{1,2}"};
1334     case "L":
1335         return {g:0,
1336             c:null,
1337             s:"(?:1|0)"};
1338     case "Y":
1339         return {g:1,
1340             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1341             s:"(\\d{4})"};
1342     case "y":
1343         return {g:1,
1344             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1345                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1346             s:"(\\d{1,2})"};
1347     case "a":
1348         return {g:1,
1349             c:"if (results[" + currentGroup + "] == 'am') {\n"
1350                 + "if (h == 12) { h = 0; }\n"
1351                 + "} else { if (h < 12) { h += 12; }}",
1352             s:"(am|pm)"};
1353     case "A":
1354         return {g:1,
1355             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1356                 + "if (h == 12) { h = 0; }\n"
1357                 + "} else { if (h < 12) { h += 12; }}",
1358             s:"(AM|PM)"};
1359     case "g":
1360     case "G":
1361         return {g:1,
1362             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1363             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1364     case "h":
1365     case "H":
1366         return {g:1,
1367             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1368             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1369     case "i":
1370         return {g:1,
1371             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1372             s:"(\\d{2})"};
1373     case "s":
1374         return {g:1,
1375             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1376             s:"(\\d{2})"};
1377     case "O":
1378         return {g:1,
1379             c:[
1380                 "o = results[", currentGroup, "];\n",
1381                 "var sn = o.substring(0,1);\n", // get + / - sign
1382                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1383                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1384                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1385                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1386             ].join(""),
1387             s:"([+\-]\\d{4})"};
1388     case "T":
1389         return {g:0,
1390             c:null,
1391             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1392     case "Z":
1393         return {g:1,
1394             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1395                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1396             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1397     default:
1398         return {g:0,
1399             c:null,
1400             s:String.escape(character)};
1401     }
1402 };
1403
1404 /**
1405  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1406  * @return {String} The abbreviated timezone name (e.g. 'CST')
1407  */
1408 Date.prototype.getTimezone = function() {
1409     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1410 };
1411
1412 /**
1413  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1414  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1415  */
1416 Date.prototype.getGMTOffset = function() {
1417     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1418         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1419         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1420 };
1421
1422 /**
1423  * Get the numeric day number of the year, adjusted for leap year.
1424  * @return {Number} 0 through 364 (365 in leap years)
1425  */
1426 Date.prototype.getDayOfYear = function() {
1427     var num = 0;
1428     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1429     for (var i = 0; i < this.getMonth(); ++i) {
1430         num += Date.daysInMonth[i];
1431     }
1432     return num + this.getDate() - 1;
1433 };
1434
1435 /**
1436  * Get the string representation of the numeric week number of the year
1437  * (equivalent to the format specifier 'W').
1438  * @return {String} '00' through '52'
1439  */
1440 Date.prototype.getWeekOfYear = function() {
1441     // Skip to Thursday of this week
1442     var now = this.getDayOfYear() + (4 - this.getDay());
1443     // Find the first Thursday of the year
1444     var jan1 = new Date(this.getFullYear(), 0, 1);
1445     var then = (7 - jan1.getDay() + 4);
1446     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1447 };
1448
1449 /**
1450  * Whether or not the current date is in a leap year.
1451  * @return {Boolean} True if the current date is in a leap year, else false
1452  */
1453 Date.prototype.isLeapYear = function() {
1454     var year = this.getFullYear();
1455     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1456 };
1457
1458 /**
1459  * Get the first day of the current month, adjusted for leap year.  The returned value
1460  * is the numeric day index within the week (0-6) which can be used in conjunction with
1461  * the {@link #monthNames} array to retrieve the textual day name.
1462  * Example:
1463  *<pre><code>
1464 var dt = new Date('1/10/2007');
1465 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1466 </code></pre>
1467  * @return {Number} The day number (0-6)
1468  */
1469 Date.prototype.getFirstDayOfMonth = function() {
1470     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1471     return (day < 0) ? (day + 7) : day;
1472 };
1473
1474 /**
1475  * Get the last day of the current month, adjusted for leap year.  The returned value
1476  * is the numeric day index within the week (0-6) which can be used in conjunction with
1477  * the {@link #monthNames} array to retrieve the textual day name.
1478  * Example:
1479  *<pre><code>
1480 var dt = new Date('1/10/2007');
1481 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1482 </code></pre>
1483  * @return {Number} The day number (0-6)
1484  */
1485 Date.prototype.getLastDayOfMonth = function() {
1486     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1487     return (day < 0) ? (day + 7) : day;
1488 };
1489
1490
1491 /**
1492  * Get the first date of this date's month
1493  * @return {Date}
1494  */
1495 Date.prototype.getFirstDateOfMonth = function() {
1496     return new Date(this.getFullYear(), this.getMonth(), 1);
1497 };
1498
1499 /**
1500  * Get the last date of this date's month
1501  * @return {Date}
1502  */
1503 Date.prototype.getLastDateOfMonth = function() {
1504     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1505 };
1506 /**
1507  * Get the number of days in the current month, adjusted for leap year.
1508  * @return {Number} The number of days in the month
1509  */
1510 Date.prototype.getDaysInMonth = function() {
1511     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1512     return Date.daysInMonth[this.getMonth()];
1513 };
1514
1515 /**
1516  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1517  * @return {String} 'st, 'nd', 'rd' or 'th'
1518  */
1519 Date.prototype.getSuffix = function() {
1520     switch (this.getDate()) {
1521         case 1:
1522         case 21:
1523         case 31:
1524             return "st";
1525         case 2:
1526         case 22:
1527             return "nd";
1528         case 3:
1529         case 23:
1530             return "rd";
1531         default:
1532             return "th";
1533     }
1534 };
1535
1536 // private
1537 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1538
1539 /**
1540  * An array of textual month names.
1541  * Override these values for international dates, for example...
1542  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1543  * @type Array
1544  * @static
1545  */
1546 Date.monthNames =
1547    ["January",
1548     "February",
1549     "March",
1550     "April",
1551     "May",
1552     "June",
1553     "July",
1554     "August",
1555     "September",
1556     "October",
1557     "November",
1558     "December"];
1559
1560 /**
1561  * An array of textual day names.
1562  * Override these values for international dates, for example...
1563  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1564  * @type Array
1565  * @static
1566  */
1567 Date.dayNames =
1568    ["Sunday",
1569     "Monday",
1570     "Tuesday",
1571     "Wednesday",
1572     "Thursday",
1573     "Friday",
1574     "Saturday"];
1575
1576 // private
1577 Date.y2kYear = 50;
1578 // private
1579 Date.monthNumbers = {
1580     Jan:0,
1581     Feb:1,
1582     Mar:2,
1583     Apr:3,
1584     May:4,
1585     Jun:5,
1586     Jul:6,
1587     Aug:7,
1588     Sep:8,
1589     Oct:9,
1590     Nov:10,
1591     Dec:11};
1592
1593 /**
1594  * Creates and returns a new Date instance with the exact same date value as the called instance.
1595  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1596  * variable will also be changed.  When the intention is to create a new variable that will not
1597  * modify the original instance, you should create a clone.
1598  *
1599  * Example of correctly cloning a date:
1600  * <pre><code>
1601 //wrong way:
1602 var orig = new Date('10/1/2006');
1603 var copy = orig;
1604 copy.setDate(5);
1605 document.write(orig);  //returns 'Thu Oct 05 2006'!
1606
1607 //correct way:
1608 var orig = new Date('10/1/2006');
1609 var copy = orig.clone();
1610 copy.setDate(5);
1611 document.write(orig);  //returns 'Thu Oct 01 2006'
1612 </code></pre>
1613  * @return {Date} The new Date instance
1614  */
1615 Date.prototype.clone = function() {
1616         return new Date(this.getTime());
1617 };
1618
1619 /**
1620  * Clears any time information from this date
1621  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1622  @return {Date} this or the clone
1623  */
1624 Date.prototype.clearTime = function(clone){
1625     if(clone){
1626         return this.clone().clearTime();
1627     }
1628     this.setHours(0);
1629     this.setMinutes(0);
1630     this.setSeconds(0);
1631     this.setMilliseconds(0);
1632     return this;
1633 };
1634
1635 // private
1636 // safari setMonth is broken
1637 if(Roo.isSafari){
1638     Date.brokenSetMonth = Date.prototype.setMonth;
1639         Date.prototype.setMonth = function(num){
1640                 if(num <= -1){
1641                         var n = Math.ceil(-num);
1642                         var back_year = Math.ceil(n/12);
1643                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1644                         this.setFullYear(this.getFullYear() - back_year);
1645                         return Date.brokenSetMonth.call(this, month);
1646                 } else {
1647                         return Date.brokenSetMonth.apply(this, arguments);
1648                 }
1649         };
1650 }
1651
1652 /** Date interval constant 
1653 * @static 
1654 * @type String */
1655 Date.MILLI = "ms";
1656 /** Date interval constant 
1657 * @static 
1658 * @type String */
1659 Date.SECOND = "s";
1660 /** Date interval constant 
1661 * @static 
1662 * @type String */
1663 Date.MINUTE = "mi";
1664 /** Date interval constant 
1665 * @static 
1666 * @type String */
1667 Date.HOUR = "h";
1668 /** Date interval constant 
1669 * @static 
1670 * @type String */
1671 Date.DAY = "d";
1672 /** Date interval constant 
1673 * @static 
1674 * @type String */
1675 Date.MONTH = "mo";
1676 /** Date interval constant 
1677 * @static 
1678 * @type String */
1679 Date.YEAR = "y";
1680
1681 /**
1682  * Provides a convenient method of performing basic date arithmetic.  This method
1683  * does not modify the Date instance being called - it creates and returns
1684  * a new Date instance containing the resulting date value.
1685  *
1686  * Examples:
1687  * <pre><code>
1688 //Basic usage:
1689 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1690 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1691
1692 //Negative values will subtract correctly:
1693 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1694 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1695
1696 //You can even chain several calls together in one line!
1697 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1698 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1699  </code></pre>
1700  *
1701  * @param {String} interval   A valid date interval enum value
1702  * @param {Number} value      The amount to add to the current date
1703  * @return {Date} The new Date instance
1704  */
1705 Date.prototype.add = function(interval, value){
1706   var d = this.clone();
1707   if (!interval || value === 0) return d;
1708   switch(interval.toLowerCase()){
1709     case Date.MILLI:
1710       d.setMilliseconds(this.getMilliseconds() + value);
1711       break;
1712     case Date.SECOND:
1713       d.setSeconds(this.getSeconds() + value);
1714       break;
1715     case Date.MINUTE:
1716       d.setMinutes(this.getMinutes() + value);
1717       break;
1718     case Date.HOUR:
1719       d.setHours(this.getHours() + value);
1720       break;
1721     case Date.DAY:
1722       d.setDate(this.getDate() + value);
1723       break;
1724     case Date.MONTH:
1725       var day = this.getDate();
1726       if(day > 28){
1727           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1728       }
1729       d.setDate(day);
1730       d.setMonth(this.getMonth() + value);
1731       break;
1732     case Date.YEAR:
1733       d.setFullYear(this.getFullYear() + value);
1734       break;
1735   }
1736   return d;
1737 };/*
1738  * Based on:
1739  * Ext JS Library 1.1.1
1740  * Copyright(c) 2006-2007, Ext JS, LLC.
1741  *
1742  * Originally Released Under LGPL - original licence link has changed is not relivant.
1743  *
1744  * Fork - LGPL
1745  * <script type="text/javascript">
1746  */
1747
1748 Roo.lib.Dom = {
1749     getViewWidth : function(full) {
1750         return full ? this.getDocumentWidth() : this.getViewportWidth();
1751     },
1752
1753     getViewHeight : function(full) {
1754         return full ? this.getDocumentHeight() : this.getViewportHeight();
1755     },
1756
1757     getDocumentHeight: function() {
1758         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1759         return Math.max(scrollHeight, this.getViewportHeight());
1760     },
1761
1762     getDocumentWidth: function() {
1763         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1764         return Math.max(scrollWidth, this.getViewportWidth());
1765     },
1766
1767     getViewportHeight: function() {
1768         var height = self.innerHeight;
1769         var mode = document.compatMode;
1770
1771         if ((mode || Roo.isIE) && !Roo.isOpera) {
1772             height = (mode == "CSS1Compat") ?
1773                      document.documentElement.clientHeight :
1774                      document.body.clientHeight;
1775         }
1776
1777         return height;
1778     },
1779
1780     getViewportWidth: function() {
1781         var width = self.innerWidth;
1782         var mode = document.compatMode;
1783
1784         if (mode || Roo.isIE) {
1785             width = (mode == "CSS1Compat") ?
1786                     document.documentElement.clientWidth :
1787                     document.body.clientWidth;
1788         }
1789         return width;
1790     },
1791
1792     isAncestor : function(p, c) {
1793         p = Roo.getDom(p);
1794         c = Roo.getDom(c);
1795         if (!p || !c) {
1796             return false;
1797         }
1798
1799         if (p.contains && !Roo.isSafari) {
1800             return p.contains(c);
1801         } else if (p.compareDocumentPosition) {
1802             return !!(p.compareDocumentPosition(c) & 16);
1803         } else {
1804             var parent = c.parentNode;
1805             while (parent) {
1806                 if (parent == p) {
1807                     return true;
1808                 }
1809                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1810                     return false;
1811                 }
1812                 parent = parent.parentNode;
1813             }
1814             return false;
1815         }
1816     },
1817
1818     getRegion : function(el) {
1819         return Roo.lib.Region.getRegion(el);
1820     },
1821
1822     getY : function(el) {
1823         return this.getXY(el)[1];
1824     },
1825
1826     getX : function(el) {
1827         return this.getXY(el)[0];
1828     },
1829
1830     getXY : function(el) {
1831         var p, pe, b, scroll, bd = document.body;
1832         el = Roo.getDom(el);
1833         var fly = Roo.lib.AnimBase.fly;
1834         if (el.getBoundingClientRect) {
1835             b = el.getBoundingClientRect();
1836             scroll = fly(document).getScroll();
1837             return [b.left + scroll.left, b.top + scroll.top];
1838         }
1839         var x = 0, y = 0;
1840
1841         p = el;
1842
1843         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1844
1845         while (p) {
1846
1847             x += p.offsetLeft;
1848             y += p.offsetTop;
1849
1850             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1851                 hasAbsolute = true;
1852             }
1853
1854             if (Roo.isGecko) {
1855                 pe = fly(p);
1856
1857                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1858                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1859
1860
1861                 x += bl;
1862                 y += bt;
1863
1864
1865                 if (p != el && pe.getStyle('overflow') != 'visible') {
1866                     x += bl;
1867                     y += bt;
1868                 }
1869             }
1870             p = p.offsetParent;
1871         }
1872
1873         if (Roo.isSafari && hasAbsolute) {
1874             x -= bd.offsetLeft;
1875             y -= bd.offsetTop;
1876         }
1877
1878         if (Roo.isGecko && !hasAbsolute) {
1879             var dbd = fly(bd);
1880             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1881             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1882         }
1883
1884         p = el.parentNode;
1885         while (p && p != bd) {
1886             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1887                 x -= p.scrollLeft;
1888                 y -= p.scrollTop;
1889             }
1890             p = p.parentNode;
1891         }
1892         return [x, y];
1893     },
1894  
1895   
1896
1897
1898     setXY : function(el, xy) {
1899         el = Roo.fly(el, '_setXY');
1900         el.position();
1901         var pts = el.translatePoints(xy);
1902         if (xy[0] !== false) {
1903             el.dom.style.left = pts.left + "px";
1904         }
1905         if (xy[1] !== false) {
1906             el.dom.style.top = pts.top + "px";
1907         }
1908     },
1909
1910     setX : function(el, x) {
1911         this.setXY(el, [x, false]);
1912     },
1913
1914     setY : function(el, y) {
1915         this.setXY(el, [false, y]);
1916     }
1917 };
1918 /*
1919  * Portions of this file are based on pieces of Yahoo User Interface Library
1920  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1921  * YUI licensed under the BSD License:
1922  * http://developer.yahoo.net/yui/license.txt
1923  * <script type="text/javascript">
1924  *
1925  */
1926
1927 Roo.lib.Event = function() {
1928     var loadComplete = false;
1929     var listeners = [];
1930     var unloadListeners = [];
1931     var retryCount = 0;
1932     var onAvailStack = [];
1933     var counter = 0;
1934     var lastError = null;
1935
1936     return {
1937         POLL_RETRYS: 200,
1938         POLL_INTERVAL: 20,
1939         EL: 0,
1940         TYPE: 1,
1941         FN: 2,
1942         WFN: 3,
1943         OBJ: 3,
1944         ADJ_SCOPE: 4,
1945         _interval: null,
1946
1947         startInterval: function() {
1948             if (!this._interval) {
1949                 var self = this;
1950                 var callback = function() {
1951                     self._tryPreloadAttach();
1952                 };
1953                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1954
1955             }
1956         },
1957
1958         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1959             onAvailStack.push({ id:         p_id,
1960                 fn:         p_fn,
1961                 obj:        p_obj,
1962                 override:   p_override,
1963                 checkReady: false    });
1964
1965             retryCount = this.POLL_RETRYS;
1966             this.startInterval();
1967         },
1968
1969
1970         addListener: function(el, eventName, fn) {
1971             el = Roo.getDom(el);
1972             if (!el || !fn) {
1973                 return false;
1974             }
1975
1976             if ("unload" == eventName) {
1977                 unloadListeners[unloadListeners.length] =
1978                 [el, eventName, fn];
1979                 return true;
1980             }
1981
1982             var wrappedFn = function(e) {
1983                 return fn(Roo.lib.Event.getEvent(e));
1984             };
1985
1986             var li = [el, eventName, fn, wrappedFn];
1987
1988             var index = listeners.length;
1989             listeners[index] = li;
1990
1991             this.doAdd(el, eventName, wrappedFn, false);
1992             return true;
1993
1994         },
1995
1996
1997         removeListener: function(el, eventName, fn) {
1998             var i, len;
1999
2000             el = Roo.getDom(el);
2001
2002             if(!fn) {
2003                 return this.purgeElement(el, false, eventName);
2004             }
2005
2006
2007             if ("unload" == eventName) {
2008
2009                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2010                     var li = unloadListeners[i];
2011                     if (li &&
2012                         li[0] == el &&
2013                         li[1] == eventName &&
2014                         li[2] == fn) {
2015                         unloadListeners.splice(i, 1);
2016                         return true;
2017                     }
2018                 }
2019
2020                 return false;
2021             }
2022
2023             var cacheItem = null;
2024
2025
2026             var index = arguments[3];
2027
2028             if ("undefined" == typeof index) {
2029                 index = this._getCacheIndex(el, eventName, fn);
2030             }
2031
2032             if (index >= 0) {
2033                 cacheItem = listeners[index];
2034             }
2035
2036             if (!el || !cacheItem) {
2037                 return false;
2038             }
2039
2040             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2041
2042             delete listeners[index][this.WFN];
2043             delete listeners[index][this.FN];
2044             listeners.splice(index, 1);
2045
2046             return true;
2047
2048         },
2049
2050
2051         getTarget: function(ev, resolveTextNode) {
2052             ev = ev.browserEvent || ev;
2053             var t = ev.target || ev.srcElement;
2054             return this.resolveTextNode(t);
2055         },
2056
2057
2058         resolveTextNode: function(node) {
2059             if (Roo.isSafari && node && 3 == node.nodeType) {
2060                 return node.parentNode;
2061             } else {
2062                 return node;
2063             }
2064         },
2065
2066
2067         getPageX: function(ev) {
2068             ev = ev.browserEvent || ev;
2069             var x = ev.pageX;
2070             if (!x && 0 !== x) {
2071                 x = ev.clientX || 0;
2072
2073                 if (Roo.isIE) {
2074                     x += this.getScroll()[1];
2075                 }
2076             }
2077
2078             return x;
2079         },
2080
2081
2082         getPageY: function(ev) {
2083             ev = ev.browserEvent || ev;
2084             var y = ev.pageY;
2085             if (!y && 0 !== y) {
2086                 y = ev.clientY || 0;
2087
2088                 if (Roo.isIE) {
2089                     y += this.getScroll()[0];
2090                 }
2091             }
2092
2093
2094             return y;
2095         },
2096
2097
2098         getXY: function(ev) {
2099             ev = ev.browserEvent || ev;
2100             return [this.getPageX(ev), this.getPageY(ev)];
2101         },
2102
2103
2104         getRelatedTarget: function(ev) {
2105             ev = ev.browserEvent || ev;
2106             var t = ev.relatedTarget;
2107             if (!t) {
2108                 if (ev.type == "mouseout") {
2109                     t = ev.toElement;
2110                 } else if (ev.type == "mouseover") {
2111                     t = ev.fromElement;
2112                 }
2113             }
2114
2115             return this.resolveTextNode(t);
2116         },
2117
2118
2119         getTime: function(ev) {
2120             ev = ev.browserEvent || ev;
2121             if (!ev.time) {
2122                 var t = new Date().getTime();
2123                 try {
2124                     ev.time = t;
2125                 } catch(ex) {
2126                     this.lastError = ex;
2127                     return t;
2128                 }
2129             }
2130
2131             return ev.time;
2132         },
2133
2134
2135         stopEvent: function(ev) {
2136             this.stopPropagation(ev);
2137             this.preventDefault(ev);
2138         },
2139
2140
2141         stopPropagation: function(ev) {
2142             ev = ev.browserEvent || ev;
2143             if (ev.stopPropagation) {
2144                 ev.stopPropagation();
2145             } else {
2146                 ev.cancelBubble = true;
2147             }
2148         },
2149
2150
2151         preventDefault: function(ev) {
2152             ev = ev.browserEvent || ev;
2153             if(ev.preventDefault) {
2154                 ev.preventDefault();
2155             } else {
2156                 ev.returnValue = false;
2157             }
2158         },
2159
2160
2161         getEvent: function(e) {
2162             var ev = e || window.event;
2163             if (!ev) {
2164                 var c = this.getEvent.caller;
2165                 while (c) {
2166                     ev = c.arguments[0];
2167                     if (ev && Event == ev.constructor) {
2168                         break;
2169                     }
2170                     c = c.caller;
2171                 }
2172             }
2173             return ev;
2174         },
2175
2176
2177         getCharCode: function(ev) {
2178             ev = ev.browserEvent || ev;
2179             return ev.charCode || ev.keyCode || 0;
2180         },
2181
2182
2183         _getCacheIndex: function(el, eventName, fn) {
2184             for (var i = 0,len = listeners.length; i < len; ++i) {
2185                 var li = listeners[i];
2186                 if (li &&
2187                     li[this.FN] == fn &&
2188                     li[this.EL] == el &&
2189                     li[this.TYPE] == eventName) {
2190                     return i;
2191                 }
2192             }
2193
2194             return -1;
2195         },
2196
2197
2198         elCache: {},
2199
2200
2201         getEl: function(id) {
2202             return document.getElementById(id);
2203         },
2204
2205
2206         clearCache: function() {
2207         },
2208
2209
2210         _load: function(e) {
2211             loadComplete = true;
2212             var EU = Roo.lib.Event;
2213
2214
2215             if (Roo.isIE) {
2216                 EU.doRemove(window, "load", EU._load);
2217             }
2218         },
2219
2220
2221         _tryPreloadAttach: function() {
2222
2223             if (this.locked) {
2224                 return false;
2225             }
2226
2227             this.locked = true;
2228
2229
2230             var tryAgain = !loadComplete;
2231             if (!tryAgain) {
2232                 tryAgain = (retryCount > 0);
2233             }
2234
2235
2236             var notAvail = [];
2237             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2238                 var item = onAvailStack[i];
2239                 if (item) {
2240                     var el = this.getEl(item.id);
2241
2242                     if (el) {
2243                         if (!item.checkReady ||
2244                             loadComplete ||
2245                             el.nextSibling ||
2246                             (document && document.body)) {
2247
2248                             var scope = el;
2249                             if (item.override) {
2250                                 if (item.override === true) {
2251                                     scope = item.obj;
2252                                 } else {
2253                                     scope = item.override;
2254                                 }
2255                             }
2256                             item.fn.call(scope, item.obj);
2257                             onAvailStack[i] = null;
2258                         }
2259                     } else {
2260                         notAvail.push(item);
2261                     }
2262                 }
2263             }
2264
2265             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2266
2267             if (tryAgain) {
2268
2269                 this.startInterval();
2270             } else {
2271                 clearInterval(this._interval);
2272                 this._interval = null;
2273             }
2274
2275             this.locked = false;
2276
2277             return true;
2278
2279         },
2280
2281
2282         purgeElement: function(el, recurse, eventName) {
2283             var elListeners = this.getListeners(el, eventName);
2284             if (elListeners) {
2285                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2286                     var l = elListeners[i];
2287                     this.removeListener(el, l.type, l.fn);
2288                 }
2289             }
2290
2291             if (recurse && el && el.childNodes) {
2292                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2293                     this.purgeElement(el.childNodes[i], recurse, eventName);
2294                 }
2295             }
2296         },
2297
2298
2299         getListeners: function(el, eventName) {
2300             var results = [], searchLists;
2301             if (!eventName) {
2302                 searchLists = [listeners, unloadListeners];
2303             } else if (eventName == "unload") {
2304                 searchLists = [unloadListeners];
2305             } else {
2306                 searchLists = [listeners];
2307             }
2308
2309             for (var j = 0; j < searchLists.length; ++j) {
2310                 var searchList = searchLists[j];
2311                 if (searchList && searchList.length > 0) {
2312                     for (var i = 0,len = searchList.length; i < len; ++i) {
2313                         var l = searchList[i];
2314                         if (l && l[this.EL] === el &&
2315                             (!eventName || eventName === l[this.TYPE])) {
2316                             results.push({
2317                                 type:   l[this.TYPE],
2318                                 fn:     l[this.FN],
2319                                 obj:    l[this.OBJ],
2320                                 adjust: l[this.ADJ_SCOPE],
2321                                 index:  i
2322                             });
2323                         }
2324                     }
2325                 }
2326             }
2327
2328             return (results.length) ? results : null;
2329         },
2330
2331
2332         _unload: function(e) {
2333
2334             var EU = Roo.lib.Event, i, j, l, len, index;
2335
2336             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2337                 l = unloadListeners[i];
2338                 if (l) {
2339                     var scope = window;
2340                     if (l[EU.ADJ_SCOPE]) {
2341                         if (l[EU.ADJ_SCOPE] === true) {
2342                             scope = l[EU.OBJ];
2343                         } else {
2344                             scope = l[EU.ADJ_SCOPE];
2345                         }
2346                     }
2347                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2348                     unloadListeners[i] = null;
2349                     l = null;
2350                     scope = null;
2351                 }
2352             }
2353
2354             unloadListeners = null;
2355
2356             if (listeners && listeners.length > 0) {
2357                 j = listeners.length;
2358                 while (j) {
2359                     index = j - 1;
2360                     l = listeners[index];
2361                     if (l) {
2362                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2363                                 l[EU.FN], index);
2364                     }
2365                     j = j - 1;
2366                 }
2367                 l = null;
2368
2369                 EU.clearCache();
2370             }
2371
2372             EU.doRemove(window, "unload", EU._unload);
2373
2374         },
2375
2376
2377         getScroll: function() {
2378             var dd = document.documentElement, db = document.body;
2379             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2380                 return [dd.scrollTop, dd.scrollLeft];
2381             } else if (db) {
2382                 return [db.scrollTop, db.scrollLeft];
2383             } else {
2384                 return [0, 0];
2385             }
2386         },
2387
2388
2389         doAdd: function () {
2390             if (window.addEventListener) {
2391                 return function(el, eventName, fn, capture) {
2392                     el.addEventListener(eventName, fn, (capture));
2393                 };
2394             } else if (window.attachEvent) {
2395                 return function(el, eventName, fn, capture) {
2396                     el.attachEvent("on" + eventName, fn);
2397                 };
2398             } else {
2399                 return function() {
2400                 };
2401             }
2402         }(),
2403
2404
2405         doRemove: function() {
2406             if (window.removeEventListener) {
2407                 return function (el, eventName, fn, capture) {
2408                     el.removeEventListener(eventName, fn, (capture));
2409                 };
2410             } else if (window.detachEvent) {
2411                 return function (el, eventName, fn) {
2412                     el.detachEvent("on" + eventName, fn);
2413                 };
2414             } else {
2415                 return function() {
2416                 };
2417             }
2418         }()
2419     };
2420     
2421 }();
2422 (function() {     
2423    
2424     var E = Roo.lib.Event;
2425     E.on = E.addListener;
2426     E.un = E.removeListener;
2427
2428     if (document && document.body) {
2429         E._load();
2430     } else {
2431         E.doAdd(window, "load", E._load);
2432     }
2433     E.doAdd(window, "unload", E._unload);
2434     E._tryPreloadAttach();
2435 })();
2436
2437 /*
2438  * Portions of this file are based on pieces of Yahoo User Interface Library
2439  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2440  * YUI licensed under the BSD License:
2441  * http://developer.yahoo.net/yui/license.txt
2442  * <script type="text/javascript">
2443  *
2444  */
2445
2446 (function() {
2447     
2448     Roo.lib.Ajax = {
2449         request : function(method, uri, cb, data, options) {
2450             if(options){
2451                 var hs = options.headers;
2452                 if(hs){
2453                     for(var h in hs){
2454                         if(hs.hasOwnProperty(h)){
2455                             this.initHeader(h, hs[h], false);
2456                         }
2457                     }
2458                 }
2459                 if(options.xmlData){
2460                     this.initHeader('Content-Type', 'text/xml', false);
2461                     method = 'POST';
2462                     data = options.xmlData;
2463                 }
2464             }
2465
2466             return this.asyncRequest(method, uri, cb, data);
2467         },
2468
2469         serializeForm : function(form) {
2470             if(typeof form == 'string') {
2471                 form = (document.getElementById(form) || document.forms[form]);
2472             }
2473
2474             var el, name, val, disabled, data = '', hasSubmit = false;
2475             for (var i = 0; i < form.elements.length; i++) {
2476                 el = form.elements[i];
2477                 disabled = form.elements[i].disabled;
2478                 name = form.elements[i].name;
2479                 val = form.elements[i].value;
2480
2481                 if (!disabled && name){
2482                     switch (el.type)
2483                             {
2484                         case 'select-one':
2485                         case 'select-multiple':
2486                             for (var j = 0; j < el.options.length; j++) {
2487                                 if (el.options[j].selected) {
2488                                     if (Roo.isIE) {
2489                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2490                                     }
2491                                     else {
2492                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2493                                     }
2494                                 }
2495                             }
2496                             break;
2497                         case 'radio':
2498                         case 'checkbox':
2499                             if (el.checked) {
2500                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2501                             }
2502                             break;
2503                         case 'file':
2504
2505                         case undefined:
2506
2507                         case 'reset':
2508
2509                         case 'button':
2510
2511                             break;
2512                         case 'submit':
2513                             if(hasSubmit == false) {
2514                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2515                                 hasSubmit = true;
2516                             }
2517                             break;
2518                         default:
2519                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2520                             break;
2521                     }
2522                 }
2523             }
2524             data = data.substr(0, data.length - 1);
2525             return data;
2526         },
2527
2528         headers:{},
2529
2530         hasHeaders:false,
2531
2532         useDefaultHeader:true,
2533
2534         defaultPostHeader:'application/x-www-form-urlencoded',
2535
2536         useDefaultXhrHeader:true,
2537
2538         defaultXhrHeader:'XMLHttpRequest',
2539
2540         hasDefaultHeaders:true,
2541
2542         defaultHeaders:{},
2543
2544         poll:{},
2545
2546         timeout:{},
2547
2548         pollInterval:50,
2549
2550         transactionId:0,
2551
2552         setProgId:function(id)
2553         {
2554             this.activeX.unshift(id);
2555         },
2556
2557         setDefaultPostHeader:function(b)
2558         {
2559             this.useDefaultHeader = b;
2560         },
2561
2562         setDefaultXhrHeader:function(b)
2563         {
2564             this.useDefaultXhrHeader = b;
2565         },
2566
2567         setPollingInterval:function(i)
2568         {
2569             if (typeof i == 'number' && isFinite(i)) {
2570                 this.pollInterval = i;
2571             }
2572         },
2573
2574         createXhrObject:function(transactionId)
2575         {
2576             var obj,http;
2577             try
2578             {
2579
2580                 http = new XMLHttpRequest();
2581
2582                 obj = { conn:http, tId:transactionId };
2583             }
2584             catch(e)
2585             {
2586                 for (var i = 0; i < this.activeX.length; ++i) {
2587                     try
2588                     {
2589
2590                         http = new ActiveXObject(this.activeX[i]);
2591
2592                         obj = { conn:http, tId:transactionId };
2593                         break;
2594                     }
2595                     catch(e) {
2596                     }
2597                 }
2598             }
2599             finally
2600             {
2601                 return obj;
2602             }
2603         },
2604
2605         getConnectionObject:function()
2606         {
2607             var o;
2608             var tId = this.transactionId;
2609
2610             try
2611             {
2612                 o = this.createXhrObject(tId);
2613                 if (o) {
2614                     this.transactionId++;
2615                 }
2616             }
2617             catch(e) {
2618             }
2619             finally
2620             {
2621                 return o;
2622             }
2623         },
2624
2625         asyncRequest:function(method, uri, callback, postData)
2626         {
2627             var o = this.getConnectionObject();
2628
2629             if (!o) {
2630                 return null;
2631             }
2632             else {
2633                 o.conn.open(method, uri, true);
2634
2635                 if (this.useDefaultXhrHeader) {
2636                     if (!this.defaultHeaders['X-Requested-With']) {
2637                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2638                     }
2639                 }
2640
2641                 if(postData && this.useDefaultHeader){
2642                     this.initHeader('Content-Type', this.defaultPostHeader);
2643                 }
2644
2645                  if (this.hasDefaultHeaders || this.hasHeaders) {
2646                     this.setHeader(o);
2647                 }
2648
2649                 this.handleReadyState(o, callback);
2650                 o.conn.send(postData || null);
2651
2652                 return o;
2653             }
2654         },
2655
2656         handleReadyState:function(o, callback)
2657         {
2658             var oConn = this;
2659
2660             if (callback && callback.timeout) {
2661                 this.timeout[o.tId] = window.setTimeout(function() {
2662                     oConn.abort(o, callback, true);
2663                 }, callback.timeout);
2664             }
2665
2666             this.poll[o.tId] = window.setInterval(
2667                     function() {
2668                         if (o.conn && o.conn.readyState == 4) {
2669                             window.clearInterval(oConn.poll[o.tId]);
2670                             delete oConn.poll[o.tId];
2671
2672                             if(callback && callback.timeout) {
2673                                 window.clearTimeout(oConn.timeout[o.tId]);
2674                                 delete oConn.timeout[o.tId];
2675                             }
2676
2677                             oConn.handleTransactionResponse(o, callback);
2678                         }
2679                     }
2680                     , this.pollInterval);
2681         },
2682
2683         handleTransactionResponse:function(o, callback, isAbort)
2684         {
2685
2686             if (!callback) {
2687                 this.releaseObject(o);
2688                 return;
2689             }
2690
2691             var httpStatus, responseObject;
2692
2693             try
2694             {
2695                 if (o.conn.status !== undefined && o.conn.status != 0) {
2696                     httpStatus = o.conn.status;
2697                 }
2698                 else {
2699                     httpStatus = 13030;
2700                 }
2701             }
2702             catch(e) {
2703
2704
2705                 httpStatus = 13030;
2706             }
2707
2708             if (httpStatus >= 200 && httpStatus < 300) {
2709                 responseObject = this.createResponseObject(o, callback.argument);
2710                 if (callback.success) {
2711                     if (!callback.scope) {
2712                         callback.success(responseObject);
2713                     }
2714                     else {
2715
2716
2717                         callback.success.apply(callback.scope, [responseObject]);
2718                     }
2719                 }
2720             }
2721             else {
2722                 switch (httpStatus) {
2723
2724                     case 12002:
2725                     case 12029:
2726                     case 12030:
2727                     case 12031:
2728                     case 12152:
2729                     case 13030:
2730                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2731                         if (callback.failure) {
2732                             if (!callback.scope) {
2733                                 callback.failure(responseObject);
2734                             }
2735                             else {
2736                                 callback.failure.apply(callback.scope, [responseObject]);
2737                             }
2738                         }
2739                         break;
2740                     default:
2741                         responseObject = this.createResponseObject(o, callback.argument);
2742                         if (callback.failure) {
2743                             if (!callback.scope) {
2744                                 callback.failure(responseObject);
2745                             }
2746                             else {
2747                                 callback.failure.apply(callback.scope, [responseObject]);
2748                             }
2749                         }
2750                 }
2751             }
2752
2753             this.releaseObject(o);
2754             responseObject = null;
2755         },
2756
2757         createResponseObject:function(o, callbackArg)
2758         {
2759             var obj = {};
2760             var headerObj = {};
2761
2762             try
2763             {
2764                 var headerStr = o.conn.getAllResponseHeaders();
2765                 var header = headerStr.split('\n');
2766                 for (var i = 0; i < header.length; i++) {
2767                     var delimitPos = header[i].indexOf(':');
2768                     if (delimitPos != -1) {
2769                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2770                     }
2771                 }
2772             }
2773             catch(e) {
2774             }
2775
2776             obj.tId = o.tId;
2777             obj.status = o.conn.status;
2778             obj.statusText = o.conn.statusText;
2779             obj.getResponseHeader = headerObj;
2780             obj.getAllResponseHeaders = headerStr;
2781             obj.responseText = o.conn.responseText;
2782             obj.responseXML = o.conn.responseXML;
2783
2784             if (typeof callbackArg !== undefined) {
2785                 obj.argument = callbackArg;
2786             }
2787
2788             return obj;
2789         },
2790
2791         createExceptionObject:function(tId, callbackArg, isAbort)
2792         {
2793             var COMM_CODE = 0;
2794             var COMM_ERROR = 'communication failure';
2795             var ABORT_CODE = -1;
2796             var ABORT_ERROR = 'transaction aborted';
2797
2798             var obj = {};
2799
2800             obj.tId = tId;
2801             if (isAbort) {
2802                 obj.status = ABORT_CODE;
2803                 obj.statusText = ABORT_ERROR;
2804             }
2805             else {
2806                 obj.status = COMM_CODE;
2807                 obj.statusText = COMM_ERROR;
2808             }
2809
2810             if (callbackArg) {
2811                 obj.argument = callbackArg;
2812             }
2813
2814             return obj;
2815         },
2816
2817         initHeader:function(label, value, isDefault)
2818         {
2819             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2820
2821             if (headerObj[label] === undefined) {
2822                 headerObj[label] = value;
2823             }
2824             else {
2825
2826
2827                 headerObj[label] = value + "," + headerObj[label];
2828             }
2829
2830             if (isDefault) {
2831                 this.hasDefaultHeaders = true;
2832             }
2833             else {
2834                 this.hasHeaders = true;
2835             }
2836         },
2837
2838
2839         setHeader:function(o)
2840         {
2841             if (this.hasDefaultHeaders) {
2842                 for (var prop in this.defaultHeaders) {
2843                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2844                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2845                     }
2846                 }
2847             }
2848
2849             if (this.hasHeaders) {
2850                 for (var prop in this.headers) {
2851                     if (this.headers.hasOwnProperty(prop)) {
2852                         o.conn.setRequestHeader(prop, this.headers[prop]);
2853                     }
2854                 }
2855                 this.headers = {};
2856                 this.hasHeaders = false;
2857             }
2858         },
2859
2860         resetDefaultHeaders:function() {
2861             delete this.defaultHeaders;
2862             this.defaultHeaders = {};
2863             this.hasDefaultHeaders = false;
2864         },
2865
2866         abort:function(o, callback, isTimeout)
2867         {
2868             if(this.isCallInProgress(o)) {
2869                 o.conn.abort();
2870                 window.clearInterval(this.poll[o.tId]);
2871                 delete this.poll[o.tId];
2872                 if (isTimeout) {
2873                     delete this.timeout[o.tId];
2874                 }
2875
2876                 this.handleTransactionResponse(o, callback, true);
2877
2878                 return true;
2879             }
2880             else {
2881                 return false;
2882             }
2883         },
2884
2885
2886         isCallInProgress:function(o)
2887         {
2888             if (o && o.conn) {
2889                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2890             }
2891             else {
2892
2893                 return false;
2894             }
2895         },
2896
2897
2898         releaseObject:function(o)
2899         {
2900
2901             o.conn = null;
2902
2903             o = null;
2904         },
2905
2906         activeX:[
2907         'MSXML2.XMLHTTP.3.0',
2908         'MSXML2.XMLHTTP',
2909         'Microsoft.XMLHTTP'
2910         ]
2911
2912
2913     };
2914 })();/*
2915  * Portions of this file are based on pieces of Yahoo User Interface Library
2916  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2917  * YUI licensed under the BSD License:
2918  * http://developer.yahoo.net/yui/license.txt
2919  * <script type="text/javascript">
2920  *
2921  */
2922
2923 Roo.lib.Region = function(t, r, b, l) {
2924     this.top = t;
2925     this[1] = t;
2926     this.right = r;
2927     this.bottom = b;
2928     this.left = l;
2929     this[0] = l;
2930 };
2931
2932
2933 Roo.lib.Region.prototype = {
2934     contains : function(region) {
2935         return ( region.left >= this.left &&
2936                  region.right <= this.right &&
2937                  region.top >= this.top &&
2938                  region.bottom <= this.bottom    );
2939
2940     },
2941
2942     getArea : function() {
2943         return ( (this.bottom - this.top) * (this.right - this.left) );
2944     },
2945
2946     intersect : function(region) {
2947         var t = Math.max(this.top, region.top);
2948         var r = Math.min(this.right, region.right);
2949         var b = Math.min(this.bottom, region.bottom);
2950         var l = Math.max(this.left, region.left);
2951
2952         if (b >= t && r >= l) {
2953             return new Roo.lib.Region(t, r, b, l);
2954         } else {
2955             return null;
2956         }
2957     },
2958     union : function(region) {
2959         var t = Math.min(this.top, region.top);
2960         var r = Math.max(this.right, region.right);
2961         var b = Math.max(this.bottom, region.bottom);
2962         var l = Math.min(this.left, region.left);
2963
2964         return new Roo.lib.Region(t, r, b, l);
2965     },
2966
2967     adjust : function(t, l, b, r) {
2968         this.top += t;
2969         this.left += l;
2970         this.right += r;
2971         this.bottom += b;
2972         return this;
2973     }
2974 };
2975
2976 Roo.lib.Region.getRegion = function(el) {
2977     var p = Roo.lib.Dom.getXY(el);
2978
2979     var t = p[1];
2980     var r = p[0] + el.offsetWidth;
2981     var b = p[1] + el.offsetHeight;
2982     var l = p[0];
2983
2984     return new Roo.lib.Region(t, r, b, l);
2985 };
2986 /*
2987  * Portions of this file are based on pieces of Yahoo User Interface Library
2988  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2989  * YUI licensed under the BSD License:
2990  * http://developer.yahoo.net/yui/license.txt
2991  * <script type="text/javascript">
2992  *
2993  */
2994 //@@dep Roo.lib.Region
2995
2996
2997 Roo.lib.Point = function(x, y) {
2998     if (x instanceof Array) {
2999         y = x[1];
3000         x = x[0];
3001     }
3002     this.x = this.right = this.left = this[0] = x;
3003     this.y = this.top = this.bottom = this[1] = y;
3004 };
3005
3006 Roo.lib.Point.prototype = new Roo.lib.Region();
3007 /*
3008  * Portions of this file are based on pieces of Yahoo User Interface Library
3009  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3010  * YUI licensed under the BSD License:
3011  * http://developer.yahoo.net/yui/license.txt
3012  * <script type="text/javascript">
3013  *
3014  */
3015  
3016 (function() {   
3017
3018     Roo.lib.Anim = {
3019         scroll : function(el, args, duration, easing, cb, scope) {
3020             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3021         },
3022
3023         motion : function(el, args, duration, easing, cb, scope) {
3024             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3025         },
3026
3027         color : function(el, args, duration, easing, cb, scope) {
3028             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3029         },
3030
3031         run : function(el, args, duration, easing, cb, scope, type) {
3032             type = type || Roo.lib.AnimBase;
3033             if (typeof easing == "string") {
3034                 easing = Roo.lib.Easing[easing];
3035             }
3036             var anim = new type(el, args, duration, easing);
3037             anim.animateX(function() {
3038                 Roo.callback(cb, scope);
3039             });
3040             return anim;
3041         }
3042     };
3043 })();/*
3044  * Portions of this file are based on pieces of Yahoo User Interface Library
3045  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3046  * YUI licensed under the BSD License:
3047  * http://developer.yahoo.net/yui/license.txt
3048  * <script type="text/javascript">
3049  *
3050  */
3051
3052 (function() {    
3053     var libFlyweight;
3054     
3055     function fly(el) {
3056         if (!libFlyweight) {
3057             libFlyweight = new Roo.Element.Flyweight();
3058         }
3059         libFlyweight.dom = el;
3060         return libFlyweight;
3061     }
3062
3063     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3064     
3065    
3066     
3067     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3068         if (el) {
3069             this.init(el, attributes, duration, method);
3070         }
3071     };
3072
3073     Roo.lib.AnimBase.fly = fly;
3074     
3075     
3076     
3077     Roo.lib.AnimBase.prototype = {
3078
3079         toString: function() {
3080             var el = this.getEl();
3081             var id = el.id || el.tagName;
3082             return ("Anim " + id);
3083         },
3084
3085         patterns: {
3086             noNegatives:        /width|height|opacity|padding/i,
3087             offsetAttribute:  /^((width|height)|(top|left))$/,
3088             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3089             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3090         },
3091
3092
3093         doMethod: function(attr, start, end) {
3094             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3095         },
3096
3097
3098         setAttribute: function(attr, val, unit) {
3099             if (this.patterns.noNegatives.test(attr)) {
3100                 val = (val > 0) ? val : 0;
3101             }
3102
3103             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3104         },
3105
3106
3107         getAttribute: function(attr) {
3108             var el = this.getEl();
3109             var val = fly(el).getStyle(attr);
3110
3111             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3112                 return parseFloat(val);
3113             }
3114
3115             var a = this.patterns.offsetAttribute.exec(attr) || [];
3116             var pos = !!( a[3] );
3117             var box = !!( a[2] );
3118
3119
3120             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3121                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3122             } else {
3123                 val = 0;
3124             }
3125
3126             return val;
3127         },
3128
3129
3130         getDefaultUnit: function(attr) {
3131             if (this.patterns.defaultUnit.test(attr)) {
3132                 return 'px';
3133             }
3134
3135             return '';
3136         },
3137
3138         animateX : function(callback, scope) {
3139             var f = function() {
3140                 this.onComplete.removeListener(f);
3141                 if (typeof callback == "function") {
3142                     callback.call(scope || this, this);
3143                 }
3144             };
3145             this.onComplete.addListener(f, this);
3146             this.animate();
3147         },
3148
3149
3150         setRuntimeAttribute: function(attr) {
3151             var start;
3152             var end;
3153             var attributes = this.attributes;
3154
3155             this.runtimeAttributes[attr] = {};
3156
3157             var isset = function(prop) {
3158                 return (typeof prop !== 'undefined');
3159             };
3160
3161             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3162                 return false;
3163             }
3164
3165             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3166
3167
3168             if (isset(attributes[attr]['to'])) {
3169                 end = attributes[attr]['to'];
3170             } else if (isset(attributes[attr]['by'])) {
3171                 if (start.constructor == Array) {
3172                     end = [];
3173                     for (var i = 0, len = start.length; i < len; ++i) {
3174                         end[i] = start[i] + attributes[attr]['by'][i];
3175                     }
3176                 } else {
3177                     end = start + attributes[attr]['by'];
3178                 }
3179             }
3180
3181             this.runtimeAttributes[attr].start = start;
3182             this.runtimeAttributes[attr].end = end;
3183
3184
3185             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3186         },
3187
3188
3189         init: function(el, attributes, duration, method) {
3190
3191             var isAnimated = false;
3192
3193
3194             var startTime = null;
3195
3196
3197             var actualFrames = 0;
3198
3199
3200             el = Roo.getDom(el);
3201
3202
3203             this.attributes = attributes || {};
3204
3205
3206             this.duration = duration || 1;
3207
3208
3209             this.method = method || Roo.lib.Easing.easeNone;
3210
3211
3212             this.useSeconds = true;
3213
3214
3215             this.currentFrame = 0;
3216
3217
3218             this.totalFrames = Roo.lib.AnimMgr.fps;
3219
3220
3221             this.getEl = function() {
3222                 return el;
3223             };
3224
3225
3226             this.isAnimated = function() {
3227                 return isAnimated;
3228             };
3229
3230
3231             this.getStartTime = function() {
3232                 return startTime;
3233             };
3234
3235             this.runtimeAttributes = {};
3236
3237
3238             this.animate = function() {
3239                 if (this.isAnimated()) {
3240                     return false;
3241                 }
3242
3243                 this.currentFrame = 0;
3244
3245                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3246
3247                 Roo.lib.AnimMgr.registerElement(this);
3248             };
3249
3250
3251             this.stop = function(finish) {
3252                 if (finish) {
3253                     this.currentFrame = this.totalFrames;
3254                     this._onTween.fire();
3255                 }
3256                 Roo.lib.AnimMgr.stop(this);
3257             };
3258
3259             var onStart = function() {
3260                 this.onStart.fire();
3261
3262                 this.runtimeAttributes = {};
3263                 for (var attr in this.attributes) {
3264                     this.setRuntimeAttribute(attr);
3265                 }
3266
3267                 isAnimated = true;
3268                 actualFrames = 0;
3269                 startTime = new Date();
3270             };
3271
3272
3273             var onTween = function() {
3274                 var data = {
3275                     duration: new Date() - this.getStartTime(),
3276                     currentFrame: this.currentFrame
3277                 };
3278
3279                 data.toString = function() {
3280                     return (
3281                             'duration: ' + data.duration +
3282                             ', currentFrame: ' + data.currentFrame
3283                             );
3284                 };
3285
3286                 this.onTween.fire(data);
3287
3288                 var runtimeAttributes = this.runtimeAttributes;
3289
3290                 for (var attr in runtimeAttributes) {
3291                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3292                 }
3293
3294                 actualFrames += 1;
3295             };
3296
3297             var onComplete = function() {
3298                 var actual_duration = (new Date() - startTime) / 1000 ;
3299
3300                 var data = {
3301                     duration: actual_duration,
3302                     frames: actualFrames,
3303                     fps: actualFrames / actual_duration
3304                 };
3305
3306                 data.toString = function() {
3307                     return (
3308                             'duration: ' + data.duration +
3309                             ', frames: ' + data.frames +
3310                             ', fps: ' + data.fps
3311                             );
3312                 };
3313
3314                 isAnimated = false;
3315                 actualFrames = 0;
3316                 this.onComplete.fire(data);
3317             };
3318
3319
3320             this._onStart = new Roo.util.Event(this);
3321             this.onStart = new Roo.util.Event(this);
3322             this.onTween = new Roo.util.Event(this);
3323             this._onTween = new Roo.util.Event(this);
3324             this.onComplete = new Roo.util.Event(this);
3325             this._onComplete = new Roo.util.Event(this);
3326             this._onStart.addListener(onStart);
3327             this._onTween.addListener(onTween);
3328             this._onComplete.addListener(onComplete);
3329         }
3330     };
3331 })();
3332 /*
3333  * Portions of this file are based on pieces of Yahoo User Interface Library
3334  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3335  * YUI licensed under the BSD License:
3336  * http://developer.yahoo.net/yui/license.txt
3337  * <script type="text/javascript">
3338  *
3339  */
3340
3341 Roo.lib.AnimMgr = new function() {
3342
3343         var thread = null;
3344
3345
3346         var queue = [];
3347
3348
3349         var tweenCount = 0;
3350
3351
3352         this.fps = 1000;
3353
3354
3355         this.delay = 1;
3356
3357
3358         this.registerElement = function(tween) {
3359             queue[queue.length] = tween;
3360             tweenCount += 1;
3361             tween._onStart.fire();
3362             this.start();
3363         };
3364
3365
3366         this.unRegister = function(tween, index) {
3367             tween._onComplete.fire();
3368             index = index || getIndex(tween);
3369             if (index != -1) {
3370                 queue.splice(index, 1);
3371             }
3372
3373             tweenCount -= 1;
3374             if (tweenCount <= 0) {
3375                 this.stop();
3376             }
3377         };
3378
3379
3380         this.start = function() {
3381             if (thread === null) {
3382                 thread = setInterval(this.run, this.delay);
3383             }
3384         };
3385
3386
3387         this.stop = function(tween) {
3388             if (!tween) {
3389                 clearInterval(thread);
3390
3391                 for (var i = 0, len = queue.length; i < len; ++i) {
3392                     if (queue[0].isAnimated()) {
3393                         this.unRegister(queue[0], 0);
3394                     }
3395                 }
3396
3397                 queue = [];
3398                 thread = null;
3399                 tweenCount = 0;
3400             }
3401             else {
3402                 this.unRegister(tween);
3403             }
3404         };
3405
3406
3407         this.run = function() {
3408             for (var i = 0, len = queue.length; i < len; ++i) {
3409                 var tween = queue[i];
3410                 if (!tween || !tween.isAnimated()) {
3411                     continue;
3412                 }
3413
3414                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3415                 {
3416                     tween.currentFrame += 1;
3417
3418                     if (tween.useSeconds) {
3419                         correctFrame(tween);
3420                     }
3421                     tween._onTween.fire();
3422                 }
3423                 else {
3424                     Roo.lib.AnimMgr.stop(tween, i);
3425                 }
3426             }
3427         };
3428
3429         var getIndex = function(anim) {
3430             for (var i = 0, len = queue.length; i < len; ++i) {
3431                 if (queue[i] == anim) {
3432                     return i;
3433                 }
3434             }
3435             return -1;
3436         };
3437
3438
3439         var correctFrame = function(tween) {
3440             var frames = tween.totalFrames;
3441             var frame = tween.currentFrame;
3442             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3443             var elapsed = (new Date() - tween.getStartTime());
3444             var tweak = 0;
3445
3446             if (elapsed < tween.duration * 1000) {
3447                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3448             } else {
3449                 tweak = frames - (frame + 1);
3450             }
3451             if (tweak > 0 && isFinite(tweak)) {
3452                 if (tween.currentFrame + tweak >= frames) {
3453                     tweak = frames - (frame + 1);
3454                 }
3455
3456                 tween.currentFrame += tweak;
3457             }
3458         };
3459     };/*
3460  * Portions of this file are based on pieces of Yahoo User Interface Library
3461  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3462  * YUI licensed under the BSD License:
3463  * http://developer.yahoo.net/yui/license.txt
3464  * <script type="text/javascript">
3465  *
3466  */
3467 Roo.lib.Bezier = new function() {
3468
3469         this.getPosition = function(points, t) {
3470             var n = points.length;
3471             var tmp = [];
3472
3473             for (var i = 0; i < n; ++i) {
3474                 tmp[i] = [points[i][0], points[i][1]];
3475             }
3476
3477             for (var j = 1; j < n; ++j) {
3478                 for (i = 0; i < n - j; ++i) {
3479                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3480                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3481                 }
3482             }
3483
3484             return [ tmp[0][0], tmp[0][1] ];
3485
3486         };
3487     };/*
3488  * Portions of this file are based on pieces of Yahoo User Interface Library
3489  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3490  * YUI licensed under the BSD License:
3491  * http://developer.yahoo.net/yui/license.txt
3492  * <script type="text/javascript">
3493  *
3494  */
3495 (function() {
3496
3497     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3498         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3499     };
3500
3501     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3502
3503     var fly = Roo.lib.AnimBase.fly;
3504     var Y = Roo.lib;
3505     var superclass = Y.ColorAnim.superclass;
3506     var proto = Y.ColorAnim.prototype;
3507
3508     proto.toString = function() {
3509         var el = this.getEl();
3510         var id = el.id || el.tagName;
3511         return ("ColorAnim " + id);
3512     };
3513
3514     proto.patterns.color = /color$/i;
3515     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3516     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3517     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3518     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3519
3520
3521     proto.parseColor = function(s) {
3522         if (s.length == 3) {
3523             return s;
3524         }
3525
3526         var c = this.patterns.hex.exec(s);
3527         if (c && c.length == 4) {
3528             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3529         }
3530
3531         c = this.patterns.rgb.exec(s);
3532         if (c && c.length == 4) {
3533             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3534         }
3535
3536         c = this.patterns.hex3.exec(s);
3537         if (c && c.length == 4) {
3538             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3539         }
3540
3541         return null;
3542     };
3543     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3544     proto.getAttribute = function(attr) {
3545         var el = this.getEl();
3546         if (this.patterns.color.test(attr)) {
3547             var val = fly(el).getStyle(attr);
3548
3549             if (this.patterns.transparent.test(val)) {
3550                 var parent = el.parentNode;
3551                 val = fly(parent).getStyle(attr);
3552
3553                 while (parent && this.patterns.transparent.test(val)) {
3554                     parent = parent.parentNode;
3555                     val = fly(parent).getStyle(attr);
3556                     if (parent.tagName.toUpperCase() == 'HTML') {
3557                         val = '#fff';
3558                     }
3559                 }
3560             }
3561         } else {
3562             val = superclass.getAttribute.call(this, attr);
3563         }
3564
3565         return val;
3566     };
3567     proto.getAttribute = function(attr) {
3568         var el = this.getEl();
3569         if (this.patterns.color.test(attr)) {
3570             var val = fly(el).getStyle(attr);
3571
3572             if (this.patterns.transparent.test(val)) {
3573                 var parent = el.parentNode;
3574                 val = fly(parent).getStyle(attr);
3575
3576                 while (parent && this.patterns.transparent.test(val)) {
3577                     parent = parent.parentNode;
3578                     val = fly(parent).getStyle(attr);
3579                     if (parent.tagName.toUpperCase() == 'HTML') {
3580                         val = '#fff';
3581                     }
3582                 }
3583             }
3584         } else {
3585             val = superclass.getAttribute.call(this, attr);
3586         }
3587
3588         return val;
3589     };
3590
3591     proto.doMethod = function(attr, start, end) {
3592         var val;
3593
3594         if (this.patterns.color.test(attr)) {
3595             val = [];
3596             for (var i = 0, len = start.length; i < len; ++i) {
3597                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3598             }
3599
3600             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3601         }
3602         else {
3603             val = superclass.doMethod.call(this, attr, start, end);
3604         }
3605
3606         return val;
3607     };
3608
3609     proto.setRuntimeAttribute = function(attr) {
3610         superclass.setRuntimeAttribute.call(this, attr);
3611
3612         if (this.patterns.color.test(attr)) {
3613             var attributes = this.attributes;
3614             var start = this.parseColor(this.runtimeAttributes[attr].start);
3615             var end = this.parseColor(this.runtimeAttributes[attr].end);
3616
3617             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3618                 end = this.parseColor(attributes[attr].by);
3619
3620                 for (var i = 0, len = start.length; i < len; ++i) {
3621                     end[i] = start[i] + end[i];
3622                 }
3623             }
3624
3625             this.runtimeAttributes[attr].start = start;
3626             this.runtimeAttributes[attr].end = end;
3627         }
3628     };
3629 })();
3630
3631 /*
3632  * Portions of this file are based on pieces of Yahoo User Interface Library
3633  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3634  * YUI licensed under the BSD License:
3635  * http://developer.yahoo.net/yui/license.txt
3636  * <script type="text/javascript">
3637  *
3638  */
3639 Roo.lib.Easing = {
3640
3641
3642     easeNone: function (t, b, c, d) {
3643         return c * t / d + b;
3644     },
3645
3646
3647     easeIn: function (t, b, c, d) {
3648         return c * (t /= d) * t + b;
3649     },
3650
3651
3652     easeOut: function (t, b, c, d) {
3653         return -c * (t /= d) * (t - 2) + b;
3654     },
3655
3656
3657     easeBoth: function (t, b, c, d) {
3658         if ((t /= d / 2) < 1) {
3659             return c / 2 * t * t + b;
3660         }
3661
3662         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3663     },
3664
3665
3666     easeInStrong: function (t, b, c, d) {
3667         return c * (t /= d) * t * t * t + b;
3668     },
3669
3670
3671     easeOutStrong: function (t, b, c, d) {
3672         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3673     },
3674
3675
3676     easeBothStrong: function (t, b, c, d) {
3677         if ((t /= d / 2) < 1) {
3678             return c / 2 * t * t * t * t + b;
3679         }
3680
3681         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3682     },
3683
3684
3685
3686     elasticIn: function (t, b, c, d, a, p) {
3687         if (t == 0) {
3688             return b;
3689         }
3690         if ((t /= d) == 1) {
3691             return b + c;
3692         }
3693         if (!p) {
3694             p = d * .3;
3695         }
3696
3697         if (!a || a < Math.abs(c)) {
3698             a = c;
3699             var s = p / 4;
3700         }
3701         else {
3702             var s = p / (2 * Math.PI) * Math.asin(c / a);
3703         }
3704
3705         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3706     },
3707
3708
3709     elasticOut: function (t, b, c, d, a, p) {
3710         if (t == 0) {
3711             return b;
3712         }
3713         if ((t /= d) == 1) {
3714             return b + c;
3715         }
3716         if (!p) {
3717             p = d * .3;
3718         }
3719
3720         if (!a || a < Math.abs(c)) {
3721             a = c;
3722             var s = p / 4;
3723         }
3724         else {
3725             var s = p / (2 * Math.PI) * Math.asin(c / a);
3726         }
3727
3728         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3729     },
3730
3731
3732     elasticBoth: function (t, b, c, d, a, p) {
3733         if (t == 0) {
3734             return b;
3735         }
3736
3737         if ((t /= d / 2) == 2) {
3738             return b + c;
3739         }
3740
3741         if (!p) {
3742             p = d * (.3 * 1.5);
3743         }
3744
3745         if (!a || a < Math.abs(c)) {
3746             a = c;
3747             var s = p / 4;
3748         }
3749         else {
3750             var s = p / (2 * Math.PI) * Math.asin(c / a);
3751         }
3752
3753         if (t < 1) {
3754             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3755                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3756         }
3757         return a * Math.pow(2, -10 * (t -= 1)) *
3758                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3759     },
3760
3761
3762
3763     backIn: function (t, b, c, d, s) {
3764         if (typeof s == 'undefined') {
3765             s = 1.70158;
3766         }
3767         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3768     },
3769
3770
3771     backOut: function (t, b, c, d, s) {
3772         if (typeof s == 'undefined') {
3773             s = 1.70158;
3774         }
3775         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3776     },
3777
3778
3779     backBoth: function (t, b, c, d, s) {
3780         if (typeof s == 'undefined') {
3781             s = 1.70158;
3782         }
3783
3784         if ((t /= d / 2 ) < 1) {
3785             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3786         }
3787         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3788     },
3789
3790
3791     bounceIn: function (t, b, c, d) {
3792         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3793     },
3794
3795
3796     bounceOut: function (t, b, c, d) {
3797         if ((t /= d) < (1 / 2.75)) {
3798             return c * (7.5625 * t * t) + b;
3799         } else if (t < (2 / 2.75)) {
3800             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3801         } else if (t < (2.5 / 2.75)) {
3802             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3803         }
3804         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3805     },
3806
3807
3808     bounceBoth: function (t, b, c, d) {
3809         if (t < d / 2) {
3810             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3811         }
3812         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3813     }
3814 };/*
3815  * Portions of this file are based on pieces of Yahoo User Interface Library
3816  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3817  * YUI licensed under the BSD License:
3818  * http://developer.yahoo.net/yui/license.txt
3819  * <script type="text/javascript">
3820  *
3821  */
3822     (function() {
3823         Roo.lib.Motion = function(el, attributes, duration, method) {
3824             if (el) {
3825                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3826             }
3827         };
3828
3829         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3830
3831
3832         var Y = Roo.lib;
3833         var superclass = Y.Motion.superclass;
3834         var proto = Y.Motion.prototype;
3835
3836         proto.toString = function() {
3837             var el = this.getEl();
3838             var id = el.id || el.tagName;
3839             return ("Motion " + id);
3840         };
3841
3842         proto.patterns.points = /^points$/i;
3843
3844         proto.setAttribute = function(attr, val, unit) {
3845             if (this.patterns.points.test(attr)) {
3846                 unit = unit || 'px';
3847                 superclass.setAttribute.call(this, 'left', val[0], unit);
3848                 superclass.setAttribute.call(this, 'top', val[1], unit);
3849             } else {
3850                 superclass.setAttribute.call(this, attr, val, unit);
3851             }
3852         };
3853
3854         proto.getAttribute = function(attr) {
3855             if (this.patterns.points.test(attr)) {
3856                 var val = [
3857                         superclass.getAttribute.call(this, 'left'),
3858                         superclass.getAttribute.call(this, 'top')
3859                         ];
3860             } else {
3861                 val = superclass.getAttribute.call(this, attr);
3862             }
3863
3864             return val;
3865         };
3866
3867         proto.doMethod = function(attr, start, end) {
3868             var val = null;
3869
3870             if (this.patterns.points.test(attr)) {
3871                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3872                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3873             } else {
3874                 val = superclass.doMethod.call(this, attr, start, end);
3875             }
3876             return val;
3877         };
3878
3879         proto.setRuntimeAttribute = function(attr) {
3880             if (this.patterns.points.test(attr)) {
3881                 var el = this.getEl();
3882                 var attributes = this.attributes;
3883                 var start;
3884                 var control = attributes['points']['control'] || [];
3885                 var end;
3886                 var i, len;
3887
3888                 if (control.length > 0 && !(control[0] instanceof Array)) {
3889                     control = [control];
3890                 } else {
3891                     var tmp = [];
3892                     for (i = 0,len = control.length; i < len; ++i) {
3893                         tmp[i] = control[i];
3894                     }
3895                     control = tmp;
3896                 }
3897
3898                 Roo.fly(el).position();
3899
3900                 if (isset(attributes['points']['from'])) {
3901                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3902                 }
3903                 else {
3904                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3905                 }
3906
3907                 start = this.getAttribute('points');
3908
3909
3910                 if (isset(attributes['points']['to'])) {
3911                     end = translateValues.call(this, attributes['points']['to'], start);
3912
3913                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3914                     for (i = 0,len = control.length; i < len; ++i) {
3915                         control[i] = translateValues.call(this, control[i], start);
3916                     }
3917
3918
3919                 } else if (isset(attributes['points']['by'])) {
3920                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3921
3922                     for (i = 0,len = control.length; i < len; ++i) {
3923                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3924                     }
3925                 }
3926
3927                 this.runtimeAttributes[attr] = [start];
3928
3929                 if (control.length > 0) {
3930                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3931                 }
3932
3933                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3934             }
3935             else {
3936                 superclass.setRuntimeAttribute.call(this, attr);
3937             }
3938         };
3939
3940         var translateValues = function(val, start) {
3941             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3942             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3943
3944             return val;
3945         };
3946
3947         var isset = function(prop) {
3948             return (typeof prop !== 'undefined');
3949         };
3950     })();
3951 /*
3952  * Portions of this file are based on pieces of Yahoo User Interface Library
3953  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3954  * YUI licensed under the BSD License:
3955  * http://developer.yahoo.net/yui/license.txt
3956  * <script type="text/javascript">
3957  *
3958  */
3959     (function() {
3960         Roo.lib.Scroll = function(el, attributes, duration, method) {
3961             if (el) {
3962                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3963             }
3964         };
3965
3966         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
3967
3968
3969         var Y = Roo.lib;
3970         var superclass = Y.Scroll.superclass;
3971         var proto = Y.Scroll.prototype;
3972
3973         proto.toString = function() {
3974             var el = this.getEl();
3975             var id = el.id || el.tagName;
3976             return ("Scroll " + id);
3977         };
3978
3979         proto.doMethod = function(attr, start, end) {
3980             var val = null;
3981
3982             if (attr == 'scroll') {
3983                 val = [
3984                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
3985                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
3986                         ];
3987
3988             } else {
3989                 val = superclass.doMethod.call(this, attr, start, end);
3990             }
3991             return val;
3992         };
3993
3994         proto.getAttribute = function(attr) {
3995             var val = null;
3996             var el = this.getEl();
3997
3998             if (attr == 'scroll') {
3999                 val = [ el.scrollLeft, el.scrollTop ];
4000             } else {
4001                 val = superclass.getAttribute.call(this, attr);
4002             }
4003
4004             return val;
4005         };
4006
4007         proto.setAttribute = function(attr, val, unit) {
4008             var el = this.getEl();
4009
4010             if (attr == 'scroll') {
4011                 el.scrollLeft = val[0];
4012                 el.scrollTop = val[1];
4013             } else {
4014                 superclass.setAttribute.call(this, attr, val, unit);
4015             }
4016         };
4017     })();
4018 /*
4019  * Based on:
4020  * Ext JS Library 1.1.1
4021  * Copyright(c) 2006-2007, Ext JS, LLC.
4022  *
4023  * Originally Released Under LGPL - original licence link has changed is not relivant.
4024  *
4025  * Fork - LGPL
4026  * <script type="text/javascript">
4027  */
4028  
4029
4030 /**
4031  * @class Roo.DomHelper
4032  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4033  * 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>.
4034  * @singleton
4035  */
4036 Roo.DomHelper = function(){
4037     var tempTableEl = null;
4038     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4039     var tableRe = /^table|tbody|tr|td$/i;
4040     var xmlns = {};
4041     // build as innerHTML where available
4042     /** @ignore */
4043     var createHtml = function(o){
4044         if(typeof o == 'string'){
4045             return o;
4046         }
4047         var b = "";
4048         if(!o.tag){
4049             o.tag = "div";
4050         }
4051         b += "<" + o.tag;
4052         for(var attr in o){
4053             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4054             if(attr == "style"){
4055                 var s = o["style"];
4056                 if(typeof s == "function"){
4057                     s = s.call();
4058                 }
4059                 if(typeof s == "string"){
4060                     b += ' style="' + s + '"';
4061                 }else if(typeof s == "object"){
4062                     b += ' style="';
4063                     for(var key in s){
4064                         if(typeof s[key] != "function"){
4065                             b += key + ":" + s[key] + ";";
4066                         }
4067                     }
4068                     b += '"';
4069                 }
4070             }else{
4071                 if(attr == "cls"){
4072                     b += ' class="' + o["cls"] + '"';
4073                 }else if(attr == "htmlFor"){
4074                     b += ' for="' + o["htmlFor"] + '"';
4075                 }else{
4076                     b += " " + attr + '="' + o[attr] + '"';
4077                 }
4078             }
4079         }
4080         if(emptyTags.test(o.tag)){
4081             b += "/>";
4082         }else{
4083             b += ">";
4084             var cn = o.children || o.cn;
4085             if(cn){
4086                 //http://bugs.kde.org/show_bug.cgi?id=71506
4087                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4088                     for(var i = 0, len = cn.length; i < len; i++) {
4089                         b += createHtml(cn[i], b);
4090                     }
4091                 }else{
4092                     b += createHtml(cn, b);
4093                 }
4094             }
4095             if(o.html){
4096                 b += o.html;
4097             }
4098             b += "</" + o.tag + ">";
4099         }
4100         return b;
4101     };
4102
4103     // build as dom
4104     /** @ignore */
4105     var createDom = function(o, parentNode){
4106          
4107         // defininition craeted..
4108         var ns = false;
4109         if (o.ns && o.ns != 'html') {
4110                
4111             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4112                 xmlns[o.ns] = o.xmlns;
4113                 ns = o.xmlns;
4114             }
4115             if (typeof(xmlns[o.ns]) == 'undefined') {
4116                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4117             }
4118             ns = xmlns[o.ns];
4119         }
4120         
4121         
4122         if (typeof(o) == 'string') {
4123             return parentNode.appendChild(document.createTextNode(o));
4124         }
4125         o.tag = o.tag || div;
4126         if (o.ns && Roo.isIE) {
4127             ns = false;
4128             o.tag = o.ns + ':' + o.tag;
4129             
4130         }
4131         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4132         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4133         for(var attr in o){
4134             
4135             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4136                     attr == "style" || typeof o[attr] == "function") continue;
4137                     
4138             if(attr=="cls" && Roo.isIE){
4139                 el.className = o["cls"];
4140             }else{
4141                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4142                 else el[attr] = o[attr];
4143             }
4144         }
4145         Roo.DomHelper.applyStyles(el, o.style);
4146         var cn = o.children || o.cn;
4147         if(cn){
4148             //http://bugs.kde.org/show_bug.cgi?id=71506
4149              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4150                 for(var i = 0, len = cn.length; i < len; i++) {
4151                     createDom(cn[i], el);
4152                 }
4153             }else{
4154                 createDom(cn, el);
4155             }
4156         }
4157         if(o.html){
4158             el.innerHTML = o.html;
4159         }
4160         if(parentNode){
4161            parentNode.appendChild(el);
4162         }
4163         return el;
4164     };
4165
4166     var ieTable = function(depth, s, h, e){
4167         tempTableEl.innerHTML = [s, h, e].join('');
4168         var i = -1, el = tempTableEl;
4169         while(++i < depth){
4170             el = el.firstChild;
4171         }
4172         return el;
4173     };
4174
4175     // kill repeat to save bytes
4176     var ts = '<table>',
4177         te = '</table>',
4178         tbs = ts+'<tbody>',
4179         tbe = '</tbody>'+te,
4180         trs = tbs + '<tr>',
4181         tre = '</tr>'+tbe;
4182
4183     /**
4184      * @ignore
4185      * Nasty code for IE's broken table implementation
4186      */
4187     var insertIntoTable = function(tag, where, el, html){
4188         if(!tempTableEl){
4189             tempTableEl = document.createElement('div');
4190         }
4191         var node;
4192         var before = null;
4193         if(tag == 'td'){
4194             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4195                 return;
4196             }
4197             if(where == 'beforebegin'){
4198                 before = el;
4199                 el = el.parentNode;
4200             } else{
4201                 before = el.nextSibling;
4202                 el = el.parentNode;
4203             }
4204             node = ieTable(4, trs, html, tre);
4205         }
4206         else if(tag == 'tr'){
4207             if(where == 'beforebegin'){
4208                 before = el;
4209                 el = el.parentNode;
4210                 node = ieTable(3, tbs, html, tbe);
4211             } else if(where == 'afterend'){
4212                 before = el.nextSibling;
4213                 el = el.parentNode;
4214                 node = ieTable(3, tbs, html, tbe);
4215             } else{ // INTO a TR
4216                 if(where == 'afterbegin'){
4217                     before = el.firstChild;
4218                 }
4219                 node = ieTable(4, trs, html, tre);
4220             }
4221         } else if(tag == 'tbody'){
4222             if(where == 'beforebegin'){
4223                 before = el;
4224                 el = el.parentNode;
4225                 node = ieTable(2, ts, html, te);
4226             } else if(where == 'afterend'){
4227                 before = el.nextSibling;
4228                 el = el.parentNode;
4229                 node = ieTable(2, ts, html, te);
4230             } else{
4231                 if(where == 'afterbegin'){
4232                     before = el.firstChild;
4233                 }
4234                 node = ieTable(3, tbs, html, tbe);
4235             }
4236         } else{ // TABLE
4237             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4238                 return;
4239             }
4240             if(where == 'afterbegin'){
4241                 before = el.firstChild;
4242             }
4243             node = ieTable(2, ts, html, te);
4244         }
4245         el.insertBefore(node, before);
4246         return node;
4247     };
4248
4249     return {
4250     /** True to force the use of DOM instead of html fragments @type Boolean */
4251     useDom : false,
4252
4253     /**
4254      * Returns the markup for the passed Element(s) config
4255      * @param {Object} o The Dom object spec (and children)
4256      * @return {String}
4257      */
4258     markup : function(o){
4259         return createHtml(o);
4260     },
4261
4262     /**
4263      * Applies a style specification to an element
4264      * @param {String/HTMLElement} el The element to apply styles to
4265      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4266      * a function which returns such a specification.
4267      */
4268     applyStyles : function(el, styles){
4269         if(styles){
4270            el = Roo.fly(el);
4271            if(typeof styles == "string"){
4272                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4273                var matches;
4274                while ((matches = re.exec(styles)) != null){
4275                    el.setStyle(matches[1], matches[2]);
4276                }
4277            }else if (typeof styles == "object"){
4278                for (var style in styles){
4279                   el.setStyle(style, styles[style]);
4280                }
4281            }else if (typeof styles == "function"){
4282                 Roo.DomHelper.applyStyles(el, styles.call());
4283            }
4284         }
4285     },
4286
4287     /**
4288      * Inserts an HTML fragment into the Dom
4289      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4290      * @param {HTMLElement} el The context element
4291      * @param {String} html The HTML fragmenet
4292      * @return {HTMLElement} The new node
4293      */
4294     insertHtml : function(where, el, html){
4295         where = where.toLowerCase();
4296         if(el.insertAdjacentHTML){
4297             if(tableRe.test(el.tagName)){
4298                 var rs;
4299                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4300                     return rs;
4301                 }
4302             }
4303             switch(where){
4304                 case "beforebegin":
4305                     el.insertAdjacentHTML('BeforeBegin', html);
4306                     return el.previousSibling;
4307                 case "afterbegin":
4308                     el.insertAdjacentHTML('AfterBegin', html);
4309                     return el.firstChild;
4310                 case "beforeend":
4311                     el.insertAdjacentHTML('BeforeEnd', html);
4312                     return el.lastChild;
4313                 case "afterend":
4314                     el.insertAdjacentHTML('AfterEnd', html);
4315                     return el.nextSibling;
4316             }
4317             throw 'Illegal insertion point -> "' + where + '"';
4318         }
4319         var range = el.ownerDocument.createRange();
4320         var frag;
4321         switch(where){
4322              case "beforebegin":
4323                 range.setStartBefore(el);
4324                 frag = range.createContextualFragment(html);
4325                 el.parentNode.insertBefore(frag, el);
4326                 return el.previousSibling;
4327              case "afterbegin":
4328                 if(el.firstChild){
4329                     range.setStartBefore(el.firstChild);
4330                     frag = range.createContextualFragment(html);
4331                     el.insertBefore(frag, el.firstChild);
4332                     return el.firstChild;
4333                 }else{
4334                     el.innerHTML = html;
4335                     return el.firstChild;
4336                 }
4337             case "beforeend":
4338                 if(el.lastChild){
4339                     range.setStartAfter(el.lastChild);
4340                     frag = range.createContextualFragment(html);
4341                     el.appendChild(frag);
4342                     return el.lastChild;
4343                 }else{
4344                     el.innerHTML = html;
4345                     return el.lastChild;
4346                 }
4347             case "afterend":
4348                 range.setStartAfter(el);
4349                 frag = range.createContextualFragment(html);
4350                 el.parentNode.insertBefore(frag, el.nextSibling);
4351                 return el.nextSibling;
4352             }
4353             throw 'Illegal insertion point -> "' + where + '"';
4354     },
4355
4356     /**
4357      * Creates new Dom element(s) and inserts them before el
4358      * @param {String/HTMLElement/Element} el The context element
4359      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4360      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4361      * @return {HTMLElement/Roo.Element} The new node
4362      */
4363     insertBefore : function(el, o, returnElement){
4364         return this.doInsert(el, o, returnElement, "beforeBegin");
4365     },
4366
4367     /**
4368      * Creates new Dom element(s) and inserts them after el
4369      * @param {String/HTMLElement/Element} el The context element
4370      * @param {Object} o The Dom object spec (and children)
4371      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4372      * @return {HTMLElement/Roo.Element} The new node
4373      */
4374     insertAfter : function(el, o, returnElement){
4375         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4376     },
4377
4378     /**
4379      * Creates new Dom element(s) and inserts them as the first child of el
4380      * @param {String/HTMLElement/Element} el The context element
4381      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4382      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4383      * @return {HTMLElement/Roo.Element} The new node
4384      */
4385     insertFirst : function(el, o, returnElement){
4386         return this.doInsert(el, o, returnElement, "afterBegin");
4387     },
4388
4389     // private
4390     doInsert : function(el, o, returnElement, pos, sibling){
4391         el = Roo.getDom(el);
4392         var newNode;
4393         if(this.useDom || o.ns){
4394             newNode = createDom(o, null);
4395             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4396         }else{
4397             var html = createHtml(o);
4398             newNode = this.insertHtml(pos, el, html);
4399         }
4400         return returnElement ? Roo.get(newNode, true) : newNode;
4401     },
4402
4403     /**
4404      * Creates new Dom element(s) and appends them to el
4405      * @param {String/HTMLElement/Element} el The context element
4406      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4407      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4408      * @return {HTMLElement/Roo.Element} The new node
4409      */
4410     append : function(el, o, returnElement){
4411         el = Roo.getDom(el);
4412         var newNode;
4413         if(this.useDom || o.ns){
4414             newNode = createDom(o, null);
4415             el.appendChild(newNode);
4416         }else{
4417             var html = createHtml(o);
4418             newNode = this.insertHtml("beforeEnd", el, html);
4419         }
4420         return returnElement ? Roo.get(newNode, true) : newNode;
4421     },
4422
4423     /**
4424      * Creates new Dom element(s) and overwrites the contents of el with them
4425      * @param {String/HTMLElement/Element} el The context element
4426      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4427      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4428      * @return {HTMLElement/Roo.Element} The new node
4429      */
4430     overwrite : function(el, o, returnElement){
4431         el = Roo.getDom(el);
4432         if (o.ns) {
4433           
4434             while (el.childNodes.length) {
4435                 el.removeChild(el.firstChild);
4436             }
4437             createDom(o, el);
4438         } else {
4439             el.innerHTML = createHtml(o);   
4440         }
4441         
4442         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4443     },
4444
4445     /**
4446      * Creates a new Roo.DomHelper.Template from the Dom object spec
4447      * @param {Object} o The Dom object spec (and children)
4448      * @return {Roo.DomHelper.Template} The new template
4449      */
4450     createTemplate : function(o){
4451         var html = createHtml(o);
4452         return new Roo.Template(html);
4453     }
4454     };
4455 }();
4456 /*
4457  * Based on:
4458  * Ext JS Library 1.1.1
4459  * Copyright(c) 2006-2007, Ext JS, LLC.
4460  *
4461  * Originally Released Under LGPL - original licence link has changed is not relivant.
4462  *
4463  * Fork - LGPL
4464  * <script type="text/javascript">
4465  */
4466  
4467 /**
4468 * @class Roo.Template
4469 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4470 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4471 * Usage:
4472 <pre><code>
4473 var t = new Roo.Template({
4474     html :  '&lt;div name="{id}"&gt;' + 
4475         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4476         '&lt;/div&gt;',
4477     myformat: function (value, allValues) {
4478         return 'XX' + value;
4479     }
4480 });
4481 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4482 </code></pre>
4483 * 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>. 
4484 * @constructor
4485 * @param {Object} cfg - Configuration object.
4486 */
4487 Roo.Template = function(cfg){
4488     // BC!
4489     if(cfg instanceof Array){
4490         cfg = cfg.join("");
4491     }else if(arguments.length > 1){
4492         cfg = Array.prototype.join.call(arguments, "");
4493     }
4494     
4495     
4496     if (typeof(cfg) == 'object') {
4497         Roo.apply(this,cfg)
4498     } else {
4499         // bc
4500         this.html = cfg;
4501     }
4502     
4503     
4504 };
4505 Roo.Template.prototype = {
4506     
4507     /**
4508      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4509      */
4510     html : '',
4511     /**
4512      * Returns an HTML fragment of this template with the specified values applied.
4513      * @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'})
4514      * @return {String} The HTML fragment
4515      */
4516     applyTemplate : function(values){
4517         try {
4518             
4519             if(this.compiled){
4520                 return this.compiled(values);
4521             }
4522             var useF = this.disableFormats !== true;
4523             var fm = Roo.util.Format, tpl = this;
4524             var fn = function(m, name, format, args){
4525                 if(format && useF){
4526                     if(format.substr(0, 5) == "this."){
4527                         return tpl.call(format.substr(5), values[name], values);
4528                     }else{
4529                         if(args){
4530                             // quoted values are required for strings in compiled templates, 
4531                             // but for non compiled we need to strip them
4532                             // quoted reversed for jsmin
4533                             var re = /^\s*['"](.*)["']\s*$/;
4534                             args = args.split(',');
4535                             for(var i = 0, len = args.length; i < len; i++){
4536                                 args[i] = args[i].replace(re, "$1");
4537                             }
4538                             args = [values[name]].concat(args);
4539                         }else{
4540                             args = [values[name]];
4541                         }
4542                         return fm[format].apply(fm, args);
4543                     }
4544                 }else{
4545                     return values[name] !== undefined ? values[name] : "";
4546                 }
4547             };
4548             return this.html.replace(this.re, fn);
4549         } catch (e) {
4550             Roo.log(e);
4551             throw e;
4552         }
4553          
4554     },
4555     
4556     /**
4557      * Sets the HTML used as the template and optionally compiles it.
4558      * @param {String} html
4559      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4560      * @return {Roo.Template} this
4561      */
4562     set : function(html, compile){
4563         this.html = html;
4564         this.compiled = null;
4565         if(compile){
4566             this.compile();
4567         }
4568         return this;
4569     },
4570     
4571     /**
4572      * True to disable format functions (defaults to false)
4573      * @type Boolean
4574      */
4575     disableFormats : false,
4576     
4577     /**
4578     * The regular expression used to match template variables 
4579     * @type RegExp
4580     * @property 
4581     */
4582     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4583     
4584     /**
4585      * Compiles the template into an internal function, eliminating the RegEx overhead.
4586      * @return {Roo.Template} this
4587      */
4588     compile : function(){
4589         var fm = Roo.util.Format;
4590         var useF = this.disableFormats !== true;
4591         var sep = Roo.isGecko ? "+" : ",";
4592         var fn = function(m, name, format, args){
4593             if(format && useF){
4594                 args = args ? ',' + args : "";
4595                 if(format.substr(0, 5) != "this."){
4596                     format = "fm." + format + '(';
4597                 }else{
4598                     format = 'this.call("'+ format.substr(5) + '", ';
4599                     args = ", values";
4600                 }
4601             }else{
4602                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4603             }
4604             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4605         };
4606         var body;
4607         // branched to use + in gecko and [].join() in others
4608         if(Roo.isGecko){
4609             body = "this.compiled = function(values){ return '" +
4610                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4611                     "';};";
4612         }else{
4613             body = ["this.compiled = function(values){ return ['"];
4614             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4615             body.push("'].join('');};");
4616             body = body.join('');
4617         }
4618         /**
4619          * eval:var:values
4620          * eval:var:fm
4621          */
4622         eval(body);
4623         return this;
4624     },
4625     
4626     // private function used to call members
4627     call : function(fnName, value, allValues){
4628         return this[fnName](value, allValues);
4629     },
4630     
4631     /**
4632      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4633      * @param {String/HTMLElement/Roo.Element} el The context element
4634      * @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'})
4635      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4636      * @return {HTMLElement/Roo.Element} The new node or Element
4637      */
4638     insertFirst: function(el, values, returnElement){
4639         return this.doInsert('afterBegin', el, values, returnElement);
4640     },
4641
4642     /**
4643      * Applies the supplied values to the template and inserts the new node(s) before el.
4644      * @param {String/HTMLElement/Roo.Element} el The context element
4645      * @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'})
4646      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4647      * @return {HTMLElement/Roo.Element} The new node or Element
4648      */
4649     insertBefore: function(el, values, returnElement){
4650         return this.doInsert('beforeBegin', el, values, returnElement);
4651     },
4652
4653     /**
4654      * Applies the supplied values to the template and inserts the new node(s) after el.
4655      * @param {String/HTMLElement/Roo.Element} el The context element
4656      * @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'})
4657      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4658      * @return {HTMLElement/Roo.Element} The new node or Element
4659      */
4660     insertAfter : function(el, values, returnElement){
4661         return this.doInsert('afterEnd', el, values, returnElement);
4662     },
4663     
4664     /**
4665      * Applies the supplied values to the template and appends the new node(s) to el.
4666      * @param {String/HTMLElement/Roo.Element} el The context element
4667      * @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'})
4668      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4669      * @return {HTMLElement/Roo.Element} The new node or Element
4670      */
4671     append : function(el, values, returnElement){
4672         return this.doInsert('beforeEnd', el, values, returnElement);
4673     },
4674
4675     doInsert : function(where, el, values, returnEl){
4676         el = Roo.getDom(el);
4677         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4678         return returnEl ? Roo.get(newNode, true) : newNode;
4679     },
4680
4681     /**
4682      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4683      * @param {String/HTMLElement/Roo.Element} el The context element
4684      * @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'})
4685      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4686      * @return {HTMLElement/Roo.Element} The new node or Element
4687      */
4688     overwrite : function(el, values, returnElement){
4689         el = Roo.getDom(el);
4690         el.innerHTML = this.applyTemplate(values);
4691         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4692     }
4693 };
4694 /**
4695  * Alias for {@link #applyTemplate}
4696  * @method
4697  */
4698 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4699
4700 // backwards compat
4701 Roo.DomHelper.Template = Roo.Template;
4702
4703 /**
4704  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4705  * @param {String/HTMLElement} el A DOM element or its id
4706  * @returns {Roo.Template} The created template
4707  * @static
4708  */
4709 Roo.Template.from = function(el){
4710     el = Roo.getDom(el);
4711     return new Roo.Template(el.value || el.innerHTML);
4712 };/*
4713  * Based on:
4714  * Ext JS Library 1.1.1
4715  * Copyright(c) 2006-2007, Ext JS, LLC.
4716  *
4717  * Originally Released Under LGPL - original licence link has changed is not relivant.
4718  *
4719  * Fork - LGPL
4720  * <script type="text/javascript">
4721  */
4722  
4723
4724 /*
4725  * This is code is also distributed under MIT license for use
4726  * with jQuery and prototype JavaScript libraries.
4727  */
4728 /**
4729  * @class Roo.DomQuery
4730 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).
4731 <p>
4732 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>
4733
4734 <p>
4735 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.
4736 </p>
4737 <h4>Element Selectors:</h4>
4738 <ul class="list">
4739     <li> <b>*</b> any element</li>
4740     <li> <b>E</b> an element with the tag E</li>
4741     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4742     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4743     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4744     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4745 </ul>
4746 <h4>Attribute Selectors:</h4>
4747 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4748 <ul class="list">
4749     <li> <b>E[foo]</b> has an attribute "foo"</li>
4750     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4751     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4752     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4753     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4754     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4755     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4756 </ul>
4757 <h4>Pseudo Classes:</h4>
4758 <ul class="list">
4759     <li> <b>E:first-child</b> E is the first child of its parent</li>
4760     <li> <b>E:last-child</b> E is the last child of its parent</li>
4761     <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>
4762     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4763     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4764     <li> <b>E:only-child</b> E is the only child of its parent</li>
4765     <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>
4766     <li> <b>E:first</b> the first E in the resultset</li>
4767     <li> <b>E:last</b> the last E in the resultset</li>
4768     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4769     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4770     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4771     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4772     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4773     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4774     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4775     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4776     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4777 </ul>
4778 <h4>CSS Value Selectors:</h4>
4779 <ul class="list">
4780     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4781     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4782     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4783     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4784     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4785     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4786 </ul>
4787  * @singleton
4788  */
4789 Roo.DomQuery = function(){
4790     var cache = {}, simpleCache = {}, valueCache = {};
4791     var nonSpace = /\S/;
4792     var trimRe = /^\s+|\s+$/g;
4793     var tplRe = /\{(\d+)\}/g;
4794     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4795     var tagTokenRe = /^(#)?([\w-\*]+)/;
4796     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4797
4798     function child(p, index){
4799         var i = 0;
4800         var n = p.firstChild;
4801         while(n){
4802             if(n.nodeType == 1){
4803                if(++i == index){
4804                    return n;
4805                }
4806             }
4807             n = n.nextSibling;
4808         }
4809         return null;
4810     };
4811
4812     function next(n){
4813         while((n = n.nextSibling) && n.nodeType != 1);
4814         return n;
4815     };
4816
4817     function prev(n){
4818         while((n = n.previousSibling) && n.nodeType != 1);
4819         return n;
4820     };
4821
4822     function children(d){
4823         var n = d.firstChild, ni = -1;
4824             while(n){
4825                 var nx = n.nextSibling;
4826                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4827                     d.removeChild(n);
4828                 }else{
4829                     n.nodeIndex = ++ni;
4830                 }
4831                 n = nx;
4832             }
4833             return this;
4834         };
4835
4836     function byClassName(c, a, v){
4837         if(!v){
4838             return c;
4839         }
4840         var r = [], ri = -1, cn;
4841         for(var i = 0, ci; ci = c[i]; i++){
4842             if((' '+ci.className+' ').indexOf(v) != -1){
4843                 r[++ri] = ci;
4844             }
4845         }
4846         return r;
4847     };
4848
4849     function attrValue(n, attr){
4850         if(!n.tagName && typeof n.length != "undefined"){
4851             n = n[0];
4852         }
4853         if(!n){
4854             return null;
4855         }
4856         if(attr == "for"){
4857             return n.htmlFor;
4858         }
4859         if(attr == "class" || attr == "className"){
4860             return n.className;
4861         }
4862         return n.getAttribute(attr) || n[attr];
4863
4864     };
4865
4866     function getNodes(ns, mode, tagName){
4867         var result = [], ri = -1, cs;
4868         if(!ns){
4869             return result;
4870         }
4871         tagName = tagName || "*";
4872         if(typeof ns.getElementsByTagName != "undefined"){
4873             ns = [ns];
4874         }
4875         if(!mode){
4876             for(var i = 0, ni; ni = ns[i]; i++){
4877                 cs = ni.getElementsByTagName(tagName);
4878                 for(var j = 0, ci; ci = cs[j]; j++){
4879                     result[++ri] = ci;
4880                 }
4881             }
4882         }else if(mode == "/" || mode == ">"){
4883             var utag = tagName.toUpperCase();
4884             for(var i = 0, ni, cn; ni = ns[i]; i++){
4885                 cn = ni.children || ni.childNodes;
4886                 for(var j = 0, cj; cj = cn[j]; j++){
4887                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4888                         result[++ri] = cj;
4889                     }
4890                 }
4891             }
4892         }else if(mode == "+"){
4893             var utag = tagName.toUpperCase();
4894             for(var i = 0, n; n = ns[i]; i++){
4895                 while((n = n.nextSibling) && n.nodeType != 1);
4896                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4897                     result[++ri] = n;
4898                 }
4899             }
4900         }else if(mode == "~"){
4901             for(var i = 0, n; n = ns[i]; i++){
4902                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4903                 if(n){
4904                     result[++ri] = n;
4905                 }
4906             }
4907         }
4908         return result;
4909     };
4910
4911     function concat(a, b){
4912         if(b.slice){
4913             return a.concat(b);
4914         }
4915         for(var i = 0, l = b.length; i < l; i++){
4916             a[a.length] = b[i];
4917         }
4918         return a;
4919     }
4920
4921     function byTag(cs, tagName){
4922         if(cs.tagName || cs == document){
4923             cs = [cs];
4924         }
4925         if(!tagName){
4926             return cs;
4927         }
4928         var r = [], ri = -1;
4929         tagName = tagName.toLowerCase();
4930         for(var i = 0, ci; ci = cs[i]; i++){
4931             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4932                 r[++ri] = ci;
4933             }
4934         }
4935         return r;
4936     };
4937
4938     function byId(cs, attr, id){
4939         if(cs.tagName || cs == document){
4940             cs = [cs];
4941         }
4942         if(!id){
4943             return cs;
4944         }
4945         var r = [], ri = -1;
4946         for(var i = 0,ci; ci = cs[i]; i++){
4947             if(ci && ci.id == id){
4948                 r[++ri] = ci;
4949                 return r;
4950             }
4951         }
4952         return r;
4953     };
4954
4955     function byAttribute(cs, attr, value, op, custom){
4956         var r = [], ri = -1, st = custom=="{";
4957         var f = Roo.DomQuery.operators[op];
4958         for(var i = 0, ci; ci = cs[i]; i++){
4959             var a;
4960             if(st){
4961                 a = Roo.DomQuery.getStyle(ci, attr);
4962             }
4963             else if(attr == "class" || attr == "className"){
4964                 a = ci.className;
4965             }else if(attr == "for"){
4966                 a = ci.htmlFor;
4967             }else if(attr == "href"){
4968                 a = ci.getAttribute("href", 2);
4969             }else{
4970                 a = ci.getAttribute(attr);
4971             }
4972             if((f && f(a, value)) || (!f && a)){
4973                 r[++ri] = ci;
4974             }
4975         }
4976         return r;
4977     };
4978
4979     function byPseudo(cs, name, value){
4980         return Roo.DomQuery.pseudos[name](cs, value);
4981     };
4982
4983     // This is for IE MSXML which does not support expandos.
4984     // IE runs the same speed using setAttribute, however FF slows way down
4985     // and Safari completely fails so they need to continue to use expandos.
4986     var isIE = window.ActiveXObject ? true : false;
4987
4988     // this eval is stop the compressor from
4989     // renaming the variable to something shorter
4990     
4991     /** eval:var:batch */
4992     var batch = 30803; 
4993
4994     var key = 30803;
4995
4996     function nodupIEXml(cs){
4997         var d = ++key;
4998         cs[0].setAttribute("_nodup", d);
4999         var r = [cs[0]];
5000         for(var i = 1, len = cs.length; i < len; i++){
5001             var c = cs[i];
5002             if(!c.getAttribute("_nodup") != d){
5003                 c.setAttribute("_nodup", d);
5004                 r[r.length] = c;
5005             }
5006         }
5007         for(var i = 0, len = cs.length; i < len; i++){
5008             cs[i].removeAttribute("_nodup");
5009         }
5010         return r;
5011     }
5012
5013     function nodup(cs){
5014         if(!cs){
5015             return [];
5016         }
5017         var len = cs.length, c, i, r = cs, cj, ri = -1;
5018         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5019             return cs;
5020         }
5021         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5022             return nodupIEXml(cs);
5023         }
5024         var d = ++key;
5025         cs[0]._nodup = d;
5026         for(i = 1; c = cs[i]; i++){
5027             if(c._nodup != d){
5028                 c._nodup = d;
5029             }else{
5030                 r = [];
5031                 for(var j = 0; j < i; j++){
5032                     r[++ri] = cs[j];
5033                 }
5034                 for(j = i+1; cj = cs[j]; j++){
5035                     if(cj._nodup != d){
5036                         cj._nodup = d;
5037                         r[++ri] = cj;
5038                     }
5039                 }
5040                 return r;
5041             }
5042         }
5043         return r;
5044     }
5045
5046     function quickDiffIEXml(c1, c2){
5047         var d = ++key;
5048         for(var i = 0, len = c1.length; i < len; i++){
5049             c1[i].setAttribute("_qdiff", d);
5050         }
5051         var r = [];
5052         for(var i = 0, len = c2.length; i < len; i++){
5053             if(c2[i].getAttribute("_qdiff") != d){
5054                 r[r.length] = c2[i];
5055             }
5056         }
5057         for(var i = 0, len = c1.length; i < len; i++){
5058            c1[i].removeAttribute("_qdiff");
5059         }
5060         return r;
5061     }
5062
5063     function quickDiff(c1, c2){
5064         var len1 = c1.length;
5065         if(!len1){
5066             return c2;
5067         }
5068         if(isIE && c1[0].selectSingleNode){
5069             return quickDiffIEXml(c1, c2);
5070         }
5071         var d = ++key;
5072         for(var i = 0; i < len1; i++){
5073             c1[i]._qdiff = d;
5074         }
5075         var r = [];
5076         for(var i = 0, len = c2.length; i < len; i++){
5077             if(c2[i]._qdiff != d){
5078                 r[r.length] = c2[i];
5079             }
5080         }
5081         return r;
5082     }
5083
5084     function quickId(ns, mode, root, id){
5085         if(ns == root){
5086            var d = root.ownerDocument || root;
5087            return d.getElementById(id);
5088         }
5089         ns = getNodes(ns, mode, "*");
5090         return byId(ns, null, id);
5091     }
5092
5093     return {
5094         getStyle : function(el, name){
5095             return Roo.fly(el).getStyle(name);
5096         },
5097         /**
5098          * Compiles a selector/xpath query into a reusable function. The returned function
5099          * takes one parameter "root" (optional), which is the context node from where the query should start.
5100          * @param {String} selector The selector/xpath query
5101          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5102          * @return {Function}
5103          */
5104         compile : function(path, type){
5105             type = type || "select";
5106             
5107             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5108             var q = path, mode, lq;
5109             var tk = Roo.DomQuery.matchers;
5110             var tklen = tk.length;
5111             var mm;
5112
5113             // accept leading mode switch
5114             var lmode = q.match(modeRe);
5115             if(lmode && lmode[1]){
5116                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5117                 q = q.replace(lmode[1], "");
5118             }
5119             // strip leading slashes
5120             while(path.substr(0, 1)=="/"){
5121                 path = path.substr(1);
5122             }
5123
5124             while(q && lq != q){
5125                 lq = q;
5126                 var tm = q.match(tagTokenRe);
5127                 if(type == "select"){
5128                     if(tm){
5129                         if(tm[1] == "#"){
5130                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5131                         }else{
5132                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5133                         }
5134                         q = q.replace(tm[0], "");
5135                     }else if(q.substr(0, 1) != '@'){
5136                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5137                     }
5138                 }else{
5139                     if(tm){
5140                         if(tm[1] == "#"){
5141                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5142                         }else{
5143                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5144                         }
5145                         q = q.replace(tm[0], "");
5146                     }
5147                 }
5148                 while(!(mm = q.match(modeRe))){
5149                     var matched = false;
5150                     for(var j = 0; j < tklen; j++){
5151                         var t = tk[j];
5152                         var m = q.match(t.re);
5153                         if(m){
5154                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5155                                                     return m[i];
5156                                                 });
5157                             q = q.replace(m[0], "");
5158                             matched = true;
5159                             break;
5160                         }
5161                     }
5162                     // prevent infinite loop on bad selector
5163                     if(!matched){
5164                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5165                     }
5166                 }
5167                 if(mm[1]){
5168                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5169                     q = q.replace(mm[1], "");
5170                 }
5171             }
5172             fn[fn.length] = "return nodup(n);\n}";
5173             
5174              /** 
5175               * list of variables that need from compression as they are used by eval.
5176              *  eval:var:batch 
5177              *  eval:var:nodup
5178              *  eval:var:byTag
5179              *  eval:var:ById
5180              *  eval:var:getNodes
5181              *  eval:var:quickId
5182              *  eval:var:mode
5183              *  eval:var:root
5184              *  eval:var:n
5185              *  eval:var:byClassName
5186              *  eval:var:byPseudo
5187              *  eval:var:byAttribute
5188              *  eval:var:attrValue
5189              * 
5190              **/ 
5191             eval(fn.join(""));
5192             return f;
5193         },
5194
5195         /**
5196          * Selects a group of elements.
5197          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5198          * @param {Node} root (optional) The start of the query (defaults to document).
5199          * @return {Array}
5200          */
5201         select : function(path, root, type){
5202             if(!root || root == document){
5203                 root = document;
5204             }
5205             if(typeof root == "string"){
5206                 root = document.getElementById(root);
5207             }
5208             var paths = path.split(",");
5209             var results = [];
5210             for(var i = 0, len = paths.length; i < len; i++){
5211                 var p = paths[i].replace(trimRe, "");
5212                 if(!cache[p]){
5213                     cache[p] = Roo.DomQuery.compile(p);
5214                     if(!cache[p]){
5215                         throw p + " is not a valid selector";
5216                     }
5217                 }
5218                 var result = cache[p](root);
5219                 if(result && result != document){
5220                     results = results.concat(result);
5221                 }
5222             }
5223             if(paths.length > 1){
5224                 return nodup(results);
5225             }
5226             return results;
5227         },
5228
5229         /**
5230          * Selects a single element.
5231          * @param {String} selector The selector/xpath query
5232          * @param {Node} root (optional) The start of the query (defaults to document).
5233          * @return {Element}
5234          */
5235         selectNode : function(path, root){
5236             return Roo.DomQuery.select(path, root)[0];
5237         },
5238
5239         /**
5240          * Selects the value of a node, optionally replacing null with the defaultValue.
5241          * @param {String} selector The selector/xpath query
5242          * @param {Node} root (optional) The start of the query (defaults to document).
5243          * @param {String} defaultValue
5244          */
5245         selectValue : function(path, root, defaultValue){
5246             path = path.replace(trimRe, "");
5247             if(!valueCache[path]){
5248                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5249             }
5250             var n = valueCache[path](root);
5251             n = n[0] ? n[0] : n;
5252             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5253             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5254         },
5255
5256         /**
5257          * Selects the value of a node, parsing integers and floats.
5258          * @param {String} selector The selector/xpath query
5259          * @param {Node} root (optional) The start of the query (defaults to document).
5260          * @param {Number} defaultValue
5261          * @return {Number}
5262          */
5263         selectNumber : function(path, root, defaultValue){
5264             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5265             return parseFloat(v);
5266         },
5267
5268         /**
5269          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5270          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5271          * @param {String} selector The simple selector to test
5272          * @return {Boolean}
5273          */
5274         is : function(el, ss){
5275             if(typeof el == "string"){
5276                 el = document.getElementById(el);
5277             }
5278             var isArray = (el instanceof Array);
5279             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5280             return isArray ? (result.length == el.length) : (result.length > 0);
5281         },
5282
5283         /**
5284          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5285          * @param {Array} el An array of elements to filter
5286          * @param {String} selector The simple selector to test
5287          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5288          * the selector instead of the ones that match
5289          * @return {Array}
5290          */
5291         filter : function(els, ss, nonMatches){
5292             ss = ss.replace(trimRe, "");
5293             if(!simpleCache[ss]){
5294                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5295             }
5296             var result = simpleCache[ss](els);
5297             return nonMatches ? quickDiff(result, els) : result;
5298         },
5299
5300         /**
5301          * Collection of matching regular expressions and code snippets.
5302          */
5303         matchers : [{
5304                 re: /^\.([\w-]+)/,
5305                 select: 'n = byClassName(n, null, " {1} ");'
5306             }, {
5307                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5308                 select: 'n = byPseudo(n, "{1}", "{2}");'
5309             },{
5310                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5311                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5312             }, {
5313                 re: /^#([\w-]+)/,
5314                 select: 'n = byId(n, null, "{1}");'
5315             },{
5316                 re: /^@([\w-]+)/,
5317                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5318             }
5319         ],
5320
5321         /**
5322          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5323          * 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;.
5324          */
5325         operators : {
5326             "=" : function(a, v){
5327                 return a == v;
5328             },
5329             "!=" : function(a, v){
5330                 return a != v;
5331             },
5332             "^=" : function(a, v){
5333                 return a && a.substr(0, v.length) == v;
5334             },
5335             "$=" : function(a, v){
5336                 return a && a.substr(a.length-v.length) == v;
5337             },
5338             "*=" : function(a, v){
5339                 return a && a.indexOf(v) !== -1;
5340             },
5341             "%=" : function(a, v){
5342                 return (a % v) == 0;
5343             },
5344             "|=" : function(a, v){
5345                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5346             },
5347             "~=" : function(a, v){
5348                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5349             }
5350         },
5351
5352         /**
5353          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5354          * and the argument (if any) supplied in the selector.
5355          */
5356         pseudos : {
5357             "first-child" : function(c){
5358                 var r = [], ri = -1, n;
5359                 for(var i = 0, ci; ci = n = c[i]; i++){
5360                     while((n = n.previousSibling) && n.nodeType != 1);
5361                     if(!n){
5362                         r[++ri] = ci;
5363                     }
5364                 }
5365                 return r;
5366             },
5367
5368             "last-child" : function(c){
5369                 var r = [], ri = -1, n;
5370                 for(var i = 0, ci; ci = n = c[i]; i++){
5371                     while((n = n.nextSibling) && n.nodeType != 1);
5372                     if(!n){
5373                         r[++ri] = ci;
5374                     }
5375                 }
5376                 return r;
5377             },
5378
5379             "nth-child" : function(c, a) {
5380                 var r = [], ri = -1;
5381                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5382                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5383                 for(var i = 0, n; n = c[i]; i++){
5384                     var pn = n.parentNode;
5385                     if (batch != pn._batch) {
5386                         var j = 0;
5387                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5388                             if(cn.nodeType == 1){
5389                                cn.nodeIndex = ++j;
5390                             }
5391                         }
5392                         pn._batch = batch;
5393                     }
5394                     if (f == 1) {
5395                         if (l == 0 || n.nodeIndex == l){
5396                             r[++ri] = n;
5397                         }
5398                     } else if ((n.nodeIndex + l) % f == 0){
5399                         r[++ri] = n;
5400                     }
5401                 }
5402
5403                 return r;
5404             },
5405
5406             "only-child" : function(c){
5407                 var r = [], ri = -1;;
5408                 for(var i = 0, ci; ci = c[i]; i++){
5409                     if(!prev(ci) && !next(ci)){
5410                         r[++ri] = ci;
5411                     }
5412                 }
5413                 return r;
5414             },
5415
5416             "empty" : function(c){
5417                 var r = [], ri = -1;
5418                 for(var i = 0, ci; ci = c[i]; i++){
5419                     var cns = ci.childNodes, j = 0, cn, empty = true;
5420                     while(cn = cns[j]){
5421                         ++j;
5422                         if(cn.nodeType == 1 || cn.nodeType == 3){
5423                             empty = false;
5424                             break;
5425                         }
5426                     }
5427                     if(empty){
5428                         r[++ri] = ci;
5429                     }
5430                 }
5431                 return r;
5432             },
5433
5434             "contains" : function(c, v){
5435                 var r = [], ri = -1;
5436                 for(var i = 0, ci; ci = c[i]; i++){
5437                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5438                         r[++ri] = ci;
5439                     }
5440                 }
5441                 return r;
5442             },
5443
5444             "nodeValue" : function(c, v){
5445                 var r = [], ri = -1;
5446                 for(var i = 0, ci; ci = c[i]; i++){
5447                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5448                         r[++ri] = ci;
5449                     }
5450                 }
5451                 return r;
5452             },
5453
5454             "checked" : function(c){
5455                 var r = [], ri = -1;
5456                 for(var i = 0, ci; ci = c[i]; i++){
5457                     if(ci.checked == true){
5458                         r[++ri] = ci;
5459                     }
5460                 }
5461                 return r;
5462             },
5463
5464             "not" : function(c, ss){
5465                 return Roo.DomQuery.filter(c, ss, true);
5466             },
5467
5468             "odd" : function(c){
5469                 return this["nth-child"](c, "odd");
5470             },
5471
5472             "even" : function(c){
5473                 return this["nth-child"](c, "even");
5474             },
5475
5476             "nth" : function(c, a){
5477                 return c[a-1] || [];
5478             },
5479
5480             "first" : function(c){
5481                 return c[0] || [];
5482             },
5483
5484             "last" : function(c){
5485                 return c[c.length-1] || [];
5486             },
5487
5488             "has" : function(c, ss){
5489                 var s = Roo.DomQuery.select;
5490                 var r = [], ri = -1;
5491                 for(var i = 0, ci; ci = c[i]; i++){
5492                     if(s(ss, ci).length > 0){
5493                         r[++ri] = ci;
5494                     }
5495                 }
5496                 return r;
5497             },
5498
5499             "next" : function(c, ss){
5500                 var is = Roo.DomQuery.is;
5501                 var r = [], ri = -1;
5502                 for(var i = 0, ci; ci = c[i]; i++){
5503                     var n = next(ci);
5504                     if(n && is(n, ss)){
5505                         r[++ri] = ci;
5506                     }
5507                 }
5508                 return r;
5509             },
5510
5511             "prev" : function(c, ss){
5512                 var is = Roo.DomQuery.is;
5513                 var r = [], ri = -1;
5514                 for(var i = 0, ci; ci = c[i]; i++){
5515                     var n = prev(ci);
5516                     if(n && is(n, ss)){
5517                         r[++ri] = ci;
5518                     }
5519                 }
5520                 return r;
5521             }
5522         }
5523     };
5524 }();
5525
5526 /**
5527  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5528  * @param {String} path The selector/xpath query
5529  * @param {Node} root (optional) The start of the query (defaults to document).
5530  * @return {Array}
5531  * @member Roo
5532  * @method query
5533  */
5534 Roo.query = Roo.DomQuery.select;
5535 /*
5536  * Based on:
5537  * Ext JS Library 1.1.1
5538  * Copyright(c) 2006-2007, Ext JS, LLC.
5539  *
5540  * Originally Released Under LGPL - original licence link has changed is not relivant.
5541  *
5542  * Fork - LGPL
5543  * <script type="text/javascript">
5544  */
5545
5546 /**
5547  * @class Roo.util.Observable
5548  * Base class that provides a common interface for publishing events. Subclasses are expected to
5549  * to have a property "events" with all the events defined.<br>
5550  * For example:
5551  * <pre><code>
5552  Employee = function(name){
5553     this.name = name;
5554     this.addEvents({
5555         "fired" : true,
5556         "quit" : true
5557     });
5558  }
5559  Roo.extend(Employee, Roo.util.Observable);
5560 </code></pre>
5561  * @param {Object} config properties to use (incuding events / listeners)
5562  */
5563
5564 Roo.util.Observable = function(cfg){
5565     
5566     cfg = cfg|| {};
5567     this.addEvents(cfg.events || {});
5568     if (cfg.events) {
5569         delete cfg.events; // make sure
5570     }
5571      
5572     Roo.apply(this, cfg);
5573     
5574     if(this.listeners){
5575         this.on(this.listeners);
5576         delete this.listeners;
5577     }
5578 };
5579 Roo.util.Observable.prototype = {
5580     /** 
5581  * @cfg {Object} listeners  list of events and functions to call for this object, 
5582  * For example :
5583  * <pre><code>
5584     listeners :  { 
5585        'click' : function(e) {
5586            ..... 
5587         } ,
5588         .... 
5589     } 
5590   </code></pre>
5591  */
5592     
5593     
5594     /**
5595      * Fires the specified event with the passed parameters (minus the event name).
5596      * @param {String} eventName
5597      * @param {Object...} args Variable number of parameters are passed to handlers
5598      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5599      */
5600     fireEvent : function(){
5601         var ce = this.events[arguments[0].toLowerCase()];
5602         if(typeof ce == "object"){
5603             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5604         }else{
5605             return true;
5606         }
5607     },
5608
5609     // private
5610     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5611
5612     /**
5613      * Appends an event handler to this component
5614      * @param {String}   eventName The type of event to listen for
5615      * @param {Function} handler The method the event invokes
5616      * @param {Object}   scope (optional) The scope in which to execute the handler
5617      * function. The handler function's "this" context.
5618      * @param {Object}   options (optional) An object containing handler configuration
5619      * properties. This may contain any of the following properties:<ul>
5620      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5621      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5622      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5623      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5624      * by the specified number of milliseconds. If the event fires again within that time, the original
5625      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5626      * </ul><br>
5627      * <p>
5628      * <b>Combining Options</b><br>
5629      * Using the options argument, it is possible to combine different types of listeners:<br>
5630      * <br>
5631      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5632                 <pre><code>
5633                 el.on('click', this.onClick, this, {
5634                         single: true,
5635                 delay: 100,
5636                 forumId: 4
5637                 });
5638                 </code></pre>
5639      * <p>
5640      * <b>Attaching multiple handlers in 1 call</b><br>
5641      * The method also allows for a single argument to be passed which is a config object containing properties
5642      * which specify multiple handlers.
5643      * <pre><code>
5644                 el.on({
5645                         'click': {
5646                         fn: this.onClick,
5647                         scope: this,
5648                         delay: 100
5649                 }, 
5650                 'mouseover': {
5651                         fn: this.onMouseOver,
5652                         scope: this
5653                 },
5654                 'mouseout': {
5655                         fn: this.onMouseOut,
5656                         scope: this
5657                 }
5658                 });
5659                 </code></pre>
5660      * <p>
5661      * Or a shorthand syntax which passes the same scope object to all handlers:
5662         <pre><code>
5663                 el.on({
5664                         'click': this.onClick,
5665                 'mouseover': this.onMouseOver,
5666                 'mouseout': this.onMouseOut,
5667                 scope: this
5668                 });
5669                 </code></pre>
5670      */
5671     addListener : function(eventName, fn, scope, o){
5672         if(typeof eventName == "object"){
5673             o = eventName;
5674             for(var e in o){
5675                 if(this.filterOptRe.test(e)){
5676                     continue;
5677                 }
5678                 if(typeof o[e] == "function"){
5679                     // shared options
5680                     this.addListener(e, o[e], o.scope,  o);
5681                 }else{
5682                     // individual options
5683                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5684                 }
5685             }
5686             return;
5687         }
5688         o = (!o || typeof o == "boolean") ? {} : o;
5689         eventName = eventName.toLowerCase();
5690         var ce = this.events[eventName] || true;
5691         if(typeof ce == "boolean"){
5692             ce = new Roo.util.Event(this, eventName);
5693             this.events[eventName] = ce;
5694         }
5695         ce.addListener(fn, scope, o);
5696     },
5697
5698     /**
5699      * Removes a listener
5700      * @param {String}   eventName     The type of event to listen for
5701      * @param {Function} handler        The handler to remove
5702      * @param {Object}   scope  (optional) The scope (this object) for the handler
5703      */
5704     removeListener : function(eventName, fn, scope){
5705         var ce = this.events[eventName.toLowerCase()];
5706         if(typeof ce == "object"){
5707             ce.removeListener(fn, scope);
5708         }
5709     },
5710
5711     /**
5712      * Removes all listeners for this object
5713      */
5714     purgeListeners : function(){
5715         for(var evt in this.events){
5716             if(typeof this.events[evt] == "object"){
5717                  this.events[evt].clearListeners();
5718             }
5719         }
5720     },
5721
5722     relayEvents : function(o, events){
5723         var createHandler = function(ename){
5724             return function(){
5725                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5726             };
5727         };
5728         for(var i = 0, len = events.length; i < len; i++){
5729             var ename = events[i];
5730             if(!this.events[ename]){ this.events[ename] = true; };
5731             o.on(ename, createHandler(ename), this);
5732         }
5733     },
5734
5735     /**
5736      * Used to define events on this Observable
5737      * @param {Object} object The object with the events defined
5738      */
5739     addEvents : function(o){
5740         if(!this.events){
5741             this.events = {};
5742         }
5743         Roo.applyIf(this.events, o);
5744     },
5745
5746     /**
5747      * Checks to see if this object has any listeners for a specified event
5748      * @param {String} eventName The name of the event to check for
5749      * @return {Boolean} True if the event is being listened for, else false
5750      */
5751     hasListener : function(eventName){
5752         var e = this.events[eventName];
5753         return typeof e == "object" && e.listeners.length > 0;
5754     }
5755 };
5756 /**
5757  * Appends an event handler to this element (shorthand for addListener)
5758  * @param {String}   eventName     The type of event to listen for
5759  * @param {Function} handler        The method the event invokes
5760  * @param {Object}   scope (optional) The scope in which to execute the handler
5761  * function. The handler function's "this" context.
5762  * @param {Object}   options  (optional)
5763  * @method
5764  */
5765 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5766 /**
5767  * Removes a listener (shorthand for removeListener)
5768  * @param {String}   eventName     The type of event to listen for
5769  * @param {Function} handler        The handler to remove
5770  * @param {Object}   scope  (optional) The scope (this object) for the handler
5771  * @method
5772  */
5773 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5774
5775 /**
5776  * Starts capture on the specified Observable. All events will be passed
5777  * to the supplied function with the event name + standard signature of the event
5778  * <b>before</b> the event is fired. If the supplied function returns false,
5779  * the event will not fire.
5780  * @param {Observable} o The Observable to capture
5781  * @param {Function} fn The function to call
5782  * @param {Object} scope (optional) The scope (this object) for the fn
5783  * @static
5784  */
5785 Roo.util.Observable.capture = function(o, fn, scope){
5786     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5787 };
5788
5789 /**
5790  * Removes <b>all</b> added captures from the Observable.
5791  * @param {Observable} o The Observable to release
5792  * @static
5793  */
5794 Roo.util.Observable.releaseCapture = function(o){
5795     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5796 };
5797
5798 (function(){
5799
5800     var createBuffered = function(h, o, scope){
5801         var task = new Roo.util.DelayedTask();
5802         return function(){
5803             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5804         };
5805     };
5806
5807     var createSingle = function(h, e, fn, scope){
5808         return function(){
5809             e.removeListener(fn, scope);
5810             return h.apply(scope, arguments);
5811         };
5812     };
5813
5814     var createDelayed = function(h, o, scope){
5815         return function(){
5816             var args = Array.prototype.slice.call(arguments, 0);
5817             setTimeout(function(){
5818                 h.apply(scope, args);
5819             }, o.delay || 10);
5820         };
5821     };
5822
5823     Roo.util.Event = function(obj, name){
5824         this.name = name;
5825         this.obj = obj;
5826         this.listeners = [];
5827     };
5828
5829     Roo.util.Event.prototype = {
5830         addListener : function(fn, scope, options){
5831             var o = options || {};
5832             scope = scope || this.obj;
5833             if(!this.isListening(fn, scope)){
5834                 var l = {fn: fn, scope: scope, options: o};
5835                 var h = fn;
5836                 if(o.delay){
5837                     h = createDelayed(h, o, scope);
5838                 }
5839                 if(o.single){
5840                     h = createSingle(h, this, fn, scope);
5841                 }
5842                 if(o.buffer){
5843                     h = createBuffered(h, o, scope);
5844                 }
5845                 l.fireFn = h;
5846                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5847                     this.listeners.push(l);
5848                 }else{
5849                     this.listeners = this.listeners.slice(0);
5850                     this.listeners.push(l);
5851                 }
5852             }
5853         },
5854
5855         findListener : function(fn, scope){
5856             scope = scope || this.obj;
5857             var ls = this.listeners;
5858             for(var i = 0, len = ls.length; i < len; i++){
5859                 var l = ls[i];
5860                 if(l.fn == fn && l.scope == scope){
5861                     return i;
5862                 }
5863             }
5864             return -1;
5865         },
5866
5867         isListening : function(fn, scope){
5868             return this.findListener(fn, scope) != -1;
5869         },
5870
5871         removeListener : function(fn, scope){
5872             var index;
5873             if((index = this.findListener(fn, scope)) != -1){
5874                 if(!this.firing){
5875                     this.listeners.splice(index, 1);
5876                 }else{
5877                     this.listeners = this.listeners.slice(0);
5878                     this.listeners.splice(index, 1);
5879                 }
5880                 return true;
5881             }
5882             return false;
5883         },
5884
5885         clearListeners : function(){
5886             this.listeners = [];
5887         },
5888
5889         fire : function(){
5890             var ls = this.listeners, scope, len = ls.length;
5891             if(len > 0){
5892                 this.firing = true;
5893                 var args = Array.prototype.slice.call(arguments, 0);
5894                 for(var i = 0; i < len; i++){
5895                     var l = ls[i];
5896                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5897                         this.firing = false;
5898                         return false;
5899                     }
5900                 }
5901                 this.firing = false;
5902             }
5903             return true;
5904         }
5905     };
5906 })();/*
5907  * Based on:
5908  * Ext JS Library 1.1.1
5909  * Copyright(c) 2006-2007, Ext JS, LLC.
5910  *
5911  * Originally Released Under LGPL - original licence link has changed is not relivant.
5912  *
5913  * Fork - LGPL
5914  * <script type="text/javascript">
5915  */
5916
5917 /**
5918  * @class Roo.EventManager
5919  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5920  * several useful events directly.
5921  * See {@link Roo.EventObject} for more details on normalized event objects.
5922  * @singleton
5923  */
5924 Roo.EventManager = function(){
5925     var docReadyEvent, docReadyProcId, docReadyState = false;
5926     var resizeEvent, resizeTask, textEvent, textSize;
5927     var E = Roo.lib.Event;
5928     var D = Roo.lib.Dom;
5929
5930
5931     var fireDocReady = function(){
5932         if(!docReadyState){
5933             docReadyState = true;
5934             Roo.isReady = true;
5935             if(docReadyProcId){
5936                 clearInterval(docReadyProcId);
5937             }
5938             if(Roo.isGecko || Roo.isOpera) {
5939                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5940             }
5941             if(Roo.isIE){
5942                 var defer = document.getElementById("ie-deferred-loader");
5943                 if(defer){
5944                     defer.onreadystatechange = null;
5945                     defer.parentNode.removeChild(defer);
5946                 }
5947             }
5948             if(docReadyEvent){
5949                 docReadyEvent.fire();
5950                 docReadyEvent.clearListeners();
5951             }
5952         }
5953     };
5954     
5955     var initDocReady = function(){
5956         docReadyEvent = new Roo.util.Event();
5957         if(Roo.isGecko || Roo.isOpera) {
5958             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5959         }else if(Roo.isIE){
5960             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5961             var defer = document.getElementById("ie-deferred-loader");
5962             defer.onreadystatechange = function(){
5963                 if(this.readyState == "complete"){
5964                     fireDocReady();
5965                 }
5966             };
5967         }else if(Roo.isSafari){ 
5968             docReadyProcId = setInterval(function(){
5969                 var rs = document.readyState;
5970                 if(rs == "complete") {
5971                     fireDocReady();     
5972                  }
5973             }, 10);
5974         }
5975         // no matter what, make sure it fires on load
5976         E.on(window, "load", fireDocReady);
5977     };
5978
5979     var createBuffered = function(h, o){
5980         var task = new Roo.util.DelayedTask(h);
5981         return function(e){
5982             // create new event object impl so new events don't wipe out properties
5983             e = new Roo.EventObjectImpl(e);
5984             task.delay(o.buffer, h, null, [e]);
5985         };
5986     };
5987
5988     var createSingle = function(h, el, ename, fn){
5989         return function(e){
5990             Roo.EventManager.removeListener(el, ename, fn);
5991             h(e);
5992         };
5993     };
5994
5995     var createDelayed = function(h, o){
5996         return function(e){
5997             // create new event object impl so new events don't wipe out properties
5998             e = new Roo.EventObjectImpl(e);
5999             setTimeout(function(){
6000                 h(e);
6001             }, o.delay || 10);
6002         };
6003     };
6004
6005     var listen = function(element, ename, opt, fn, scope){
6006         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6007         fn = fn || o.fn; scope = scope || o.scope;
6008         var el = Roo.getDom(element);
6009         if(!el){
6010             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6011         }
6012         var h = function(e){
6013             e = Roo.EventObject.setEvent(e);
6014             var t;
6015             if(o.delegate){
6016                 t = e.getTarget(o.delegate, el);
6017                 if(!t){
6018                     return;
6019                 }
6020             }else{
6021                 t = e.target;
6022             }
6023             if(o.stopEvent === true){
6024                 e.stopEvent();
6025             }
6026             if(o.preventDefault === true){
6027                e.preventDefault();
6028             }
6029             if(o.stopPropagation === true){
6030                 e.stopPropagation();
6031             }
6032
6033             if(o.normalized === false){
6034                 e = e.browserEvent;
6035             }
6036
6037             fn.call(scope || el, e, t, o);
6038         };
6039         if(o.delay){
6040             h = createDelayed(h, o);
6041         }
6042         if(o.single){
6043             h = createSingle(h, el, ename, fn);
6044         }
6045         if(o.buffer){
6046             h = createBuffered(h, o);
6047         }
6048         fn._handlers = fn._handlers || [];
6049         fn._handlers.push([Roo.id(el), ename, h]);
6050
6051         E.on(el, ename, h);
6052         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6053             el.addEventListener("DOMMouseScroll", h, false);
6054             E.on(window, 'unload', function(){
6055                 el.removeEventListener("DOMMouseScroll", h, false);
6056             });
6057         }
6058         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6059             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6060         }
6061         return h;
6062     };
6063
6064     var stopListening = function(el, ename, fn){
6065         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6066         if(hds){
6067             for(var i = 0, len = hds.length; i < len; i++){
6068                 var h = hds[i];
6069                 if(h[0] == id && h[1] == ename){
6070                     hd = h[2];
6071                     hds.splice(i, 1);
6072                     break;
6073                 }
6074             }
6075         }
6076         E.un(el, ename, hd);
6077         el = Roo.getDom(el);
6078         if(ename == "mousewheel" && el.addEventListener){
6079             el.removeEventListener("DOMMouseScroll", hd, false);
6080         }
6081         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6082             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6083         }
6084     };
6085
6086     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6087     
6088     var pub = {
6089         
6090         
6091         /** 
6092          * Fix for doc tools
6093          * @scope Roo.EventManager
6094          */
6095         
6096         
6097         /** 
6098          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6099          * object with a Roo.EventObject
6100          * @param {Function} fn        The method the event invokes
6101          * @param {Object}   scope    An object that becomes the scope of the handler
6102          * @param {boolean}  override If true, the obj passed in becomes
6103          *                             the execution scope of the listener
6104          * @return {Function} The wrapped function
6105          * @deprecated
6106          */
6107         wrap : function(fn, scope, override){
6108             return function(e){
6109                 Roo.EventObject.setEvent(e);
6110                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6111             };
6112         },
6113         
6114         /**
6115      * Appends an event handler to an element (shorthand for addListener)
6116      * @param {String/HTMLElement}   element        The html element or id to assign the
6117      * @param {String}   eventName The type of event to listen for
6118      * @param {Function} handler The method the event invokes
6119      * @param {Object}   scope (optional) The scope in which to execute the handler
6120      * function. The handler function's "this" context.
6121      * @param {Object}   options (optional) An object containing handler configuration
6122      * properties. This may contain any of the following properties:<ul>
6123      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6124      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6125      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6126      * <li>preventDefault {Boolean} True to prevent the default action</li>
6127      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6128      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6129      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6130      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6131      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6132      * by the specified number of milliseconds. If the event fires again within that time, the original
6133      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6134      * </ul><br>
6135      * <p>
6136      * <b>Combining Options</b><br>
6137      * Using the options argument, it is possible to combine different types of listeners:<br>
6138      * <br>
6139      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6140      * Code:<pre><code>
6141 el.on('click', this.onClick, this, {
6142     single: true,
6143     delay: 100,
6144     stopEvent : true,
6145     forumId: 4
6146 });</code></pre>
6147      * <p>
6148      * <b>Attaching multiple handlers in 1 call</b><br>
6149       * The method also allows for a single argument to be passed which is a config object containing properties
6150      * which specify multiple handlers.
6151      * <p>
6152      * Code:<pre><code>
6153 el.on({
6154     'click' : {
6155         fn: this.onClick
6156         scope: this,
6157         delay: 100
6158     },
6159     'mouseover' : {
6160         fn: this.onMouseOver
6161         scope: this
6162     },
6163     'mouseout' : {
6164         fn: this.onMouseOut
6165         scope: this
6166     }
6167 });</code></pre>
6168      * <p>
6169      * Or a shorthand syntax:<br>
6170      * Code:<pre><code>
6171 el.on({
6172     'click' : this.onClick,
6173     'mouseover' : this.onMouseOver,
6174     'mouseout' : this.onMouseOut
6175     scope: this
6176 });</code></pre>
6177      */
6178         addListener : function(element, eventName, fn, scope, options){
6179             if(typeof eventName == "object"){
6180                 var o = eventName;
6181                 for(var e in o){
6182                     if(propRe.test(e)){
6183                         continue;
6184                     }
6185                     if(typeof o[e] == "function"){
6186                         // shared options
6187                         listen(element, e, o, o[e], o.scope);
6188                     }else{
6189                         // individual options
6190                         listen(element, e, o[e]);
6191                     }
6192                 }
6193                 return;
6194             }
6195             return listen(element, eventName, options, fn, scope);
6196         },
6197         
6198         /**
6199          * Removes an event handler
6200          *
6201          * @param {String/HTMLElement}   element        The id or html element to remove the 
6202          *                             event from
6203          * @param {String}   eventName     The type of event
6204          * @param {Function} fn
6205          * @return {Boolean} True if a listener was actually removed
6206          */
6207         removeListener : function(element, eventName, fn){
6208             return stopListening(element, eventName, fn);
6209         },
6210         
6211         /**
6212          * Fires when the document is ready (before onload and before images are loaded). Can be 
6213          * accessed shorthanded Roo.onReady().
6214          * @param {Function} fn        The method the event invokes
6215          * @param {Object}   scope    An  object that becomes the scope of the handler
6216          * @param {boolean}  options
6217          */
6218         onDocumentReady : function(fn, scope, options){
6219             if(docReadyState){ // if it already fired
6220                 docReadyEvent.addListener(fn, scope, options);
6221                 docReadyEvent.fire();
6222                 docReadyEvent.clearListeners();
6223                 return;
6224             }
6225             if(!docReadyEvent){
6226                 initDocReady();
6227             }
6228             docReadyEvent.addListener(fn, scope, options);
6229         },
6230         
6231         /**
6232          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6233          * @param {Function} fn        The method the event invokes
6234          * @param {Object}   scope    An object that becomes the scope of the handler
6235          * @param {boolean}  options
6236          */
6237         onWindowResize : function(fn, scope, options){
6238             if(!resizeEvent){
6239                 resizeEvent = new Roo.util.Event();
6240                 resizeTask = new Roo.util.DelayedTask(function(){
6241                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6242                 });
6243                 E.on(window, "resize", function(){
6244                     if(Roo.isIE){
6245                         resizeTask.delay(50);
6246                     }else{
6247                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6248                     }
6249                 });
6250             }
6251             resizeEvent.addListener(fn, scope, options);
6252         },
6253
6254         /**
6255          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6256          * @param {Function} fn        The method the event invokes
6257          * @param {Object}   scope    An object that becomes the scope of the handler
6258          * @param {boolean}  options
6259          */
6260         onTextResize : function(fn, scope, options){
6261             if(!textEvent){
6262                 textEvent = new Roo.util.Event();
6263                 var textEl = new Roo.Element(document.createElement('div'));
6264                 textEl.dom.className = 'x-text-resize';
6265                 textEl.dom.innerHTML = 'X';
6266                 textEl.appendTo(document.body);
6267                 textSize = textEl.dom.offsetHeight;
6268                 setInterval(function(){
6269                     if(textEl.dom.offsetHeight != textSize){
6270                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6271                     }
6272                 }, this.textResizeInterval);
6273             }
6274             textEvent.addListener(fn, scope, options);
6275         },
6276
6277         /**
6278          * Removes the passed window resize listener.
6279          * @param {Function} fn        The method the event invokes
6280          * @param {Object}   scope    The scope of handler
6281          */
6282         removeResizeListener : function(fn, scope){
6283             if(resizeEvent){
6284                 resizeEvent.removeListener(fn, scope);
6285             }
6286         },
6287
6288         // private
6289         fireResize : function(){
6290             if(resizeEvent){
6291                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6292             }   
6293         },
6294         /**
6295          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6296          */
6297         ieDeferSrc : false,
6298         /**
6299          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6300          */
6301         textResizeInterval : 50
6302     };
6303     
6304     /**
6305      * Fix for doc tools
6306      * @scopeAlias pub=Roo.EventManager
6307      */
6308     
6309      /**
6310      * Appends an event handler to an element (shorthand for addListener)
6311      * @param {String/HTMLElement}   element        The html element or id to assign the
6312      * @param {String}   eventName The type of event to listen for
6313      * @param {Function} handler The method the event invokes
6314      * @param {Object}   scope (optional) The scope in which to execute the handler
6315      * function. The handler function's "this" context.
6316      * @param {Object}   options (optional) An object containing handler configuration
6317      * properties. This may contain any of the following properties:<ul>
6318      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6319      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6320      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6321      * <li>preventDefault {Boolean} True to prevent the default action</li>
6322      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6323      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6324      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6325      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6326      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6327      * by the specified number of milliseconds. If the event fires again within that time, the original
6328      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6329      * </ul><br>
6330      * <p>
6331      * <b>Combining Options</b><br>
6332      * Using the options argument, it is possible to combine different types of listeners:<br>
6333      * <br>
6334      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6335      * Code:<pre><code>
6336 el.on('click', this.onClick, this, {
6337     single: true,
6338     delay: 100,
6339     stopEvent : true,
6340     forumId: 4
6341 });</code></pre>
6342      * <p>
6343      * <b>Attaching multiple handlers in 1 call</b><br>
6344       * The method also allows for a single argument to be passed which is a config object containing properties
6345      * which specify multiple handlers.
6346      * <p>
6347      * Code:<pre><code>
6348 el.on({
6349     'click' : {
6350         fn: this.onClick
6351         scope: this,
6352         delay: 100
6353     },
6354     'mouseover' : {
6355         fn: this.onMouseOver
6356         scope: this
6357     },
6358     'mouseout' : {
6359         fn: this.onMouseOut
6360         scope: this
6361     }
6362 });</code></pre>
6363      * <p>
6364      * Or a shorthand syntax:<br>
6365      * Code:<pre><code>
6366 el.on({
6367     'click' : this.onClick,
6368     'mouseover' : this.onMouseOver,
6369     'mouseout' : this.onMouseOut
6370     scope: this
6371 });</code></pre>
6372      */
6373     pub.on = pub.addListener;
6374     pub.un = pub.removeListener;
6375
6376     pub.stoppedMouseDownEvent = new Roo.util.Event();
6377     return pub;
6378 }();
6379 /**
6380   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6381   * @param {Function} fn        The method the event invokes
6382   * @param {Object}   scope    An  object that becomes the scope of the handler
6383   * @param {boolean}  override If true, the obj passed in becomes
6384   *                             the execution scope of the listener
6385   * @member Roo
6386   * @method onReady
6387  */
6388 Roo.onReady = Roo.EventManager.onDocumentReady;
6389
6390 Roo.onReady(function(){
6391     var bd = Roo.get(document.body);
6392     if(!bd){ return; }
6393
6394     var cls = [
6395             Roo.isIE ? "roo-ie"
6396             : Roo.isGecko ? "roo-gecko"
6397             : Roo.isOpera ? "roo-opera"
6398             : Roo.isSafari ? "roo-safari" : ""];
6399
6400     if(Roo.isMac){
6401         cls.push("roo-mac");
6402     }
6403     if(Roo.isLinux){
6404         cls.push("roo-linux");
6405     }
6406     if(Roo.isBorderBox){
6407         cls.push('roo-border-box');
6408     }
6409     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6410         var p = bd.dom.parentNode;
6411         if(p){
6412             p.className += ' roo-strict';
6413         }
6414     }
6415     bd.addClass(cls.join(' '));
6416 });
6417
6418 /**
6419  * @class Roo.EventObject
6420  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6421  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6422  * Example:
6423  * <pre><code>
6424  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6425     e.preventDefault();
6426     var target = e.getTarget();
6427     ...
6428  }
6429  var myDiv = Roo.get("myDiv");
6430  myDiv.on("click", handleClick);
6431  //or
6432  Roo.EventManager.on("myDiv", 'click', handleClick);
6433  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6434  </code></pre>
6435  * @singleton
6436  */
6437 Roo.EventObject = function(){
6438     
6439     var E = Roo.lib.Event;
6440     
6441     // safari keypress events for special keys return bad keycodes
6442     var safariKeys = {
6443         63234 : 37, // left
6444         63235 : 39, // right
6445         63232 : 38, // up
6446         63233 : 40, // down
6447         63276 : 33, // page up
6448         63277 : 34, // page down
6449         63272 : 46, // delete
6450         63273 : 36, // home
6451         63275 : 35  // end
6452     };
6453
6454     // normalize button clicks
6455     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6456                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6457
6458     Roo.EventObjectImpl = function(e){
6459         if(e){
6460             this.setEvent(e.browserEvent || e);
6461         }
6462     };
6463     Roo.EventObjectImpl.prototype = {
6464         /**
6465          * Used to fix doc tools.
6466          * @scope Roo.EventObject.prototype
6467          */
6468             
6469
6470         
6471         
6472         /** The normal browser event */
6473         browserEvent : null,
6474         /** The button pressed in a mouse event */
6475         button : -1,
6476         /** True if the shift key was down during the event */
6477         shiftKey : false,
6478         /** True if the control key was down during the event */
6479         ctrlKey : false,
6480         /** True if the alt key was down during the event */
6481         altKey : false,
6482
6483         /** Key constant 
6484         * @type Number */
6485         BACKSPACE : 8,
6486         /** Key constant 
6487         * @type Number */
6488         TAB : 9,
6489         /** Key constant 
6490         * @type Number */
6491         RETURN : 13,
6492         /** Key constant 
6493         * @type Number */
6494         ENTER : 13,
6495         /** Key constant 
6496         * @type Number */
6497         SHIFT : 16,
6498         /** Key constant 
6499         * @type Number */
6500         CONTROL : 17,
6501         /** Key constant 
6502         * @type Number */
6503         ESC : 27,
6504         /** Key constant 
6505         * @type Number */
6506         SPACE : 32,
6507         /** Key constant 
6508         * @type Number */
6509         PAGEUP : 33,
6510         /** Key constant 
6511         * @type Number */
6512         PAGEDOWN : 34,
6513         /** Key constant 
6514         * @type Number */
6515         END : 35,
6516         /** Key constant 
6517         * @type Number */
6518         HOME : 36,
6519         /** Key constant 
6520         * @type Number */
6521         LEFT : 37,
6522         /** Key constant 
6523         * @type Number */
6524         UP : 38,
6525         /** Key constant 
6526         * @type Number */
6527         RIGHT : 39,
6528         /** Key constant 
6529         * @type Number */
6530         DOWN : 40,
6531         /** Key constant 
6532         * @type Number */
6533         DELETE : 46,
6534         /** Key constant 
6535         * @type Number */
6536         F5 : 116,
6537
6538            /** @private */
6539         setEvent : function(e){
6540             if(e == this || (e && e.browserEvent)){ // already wrapped
6541                 return e;
6542             }
6543             this.browserEvent = e;
6544             if(e){
6545                 // normalize buttons
6546                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6547                 if(e.type == 'click' && this.button == -1){
6548                     this.button = 0;
6549                 }
6550                 this.type = e.type;
6551                 this.shiftKey = e.shiftKey;
6552                 // mac metaKey behaves like ctrlKey
6553                 this.ctrlKey = e.ctrlKey || e.metaKey;
6554                 this.altKey = e.altKey;
6555                 // in getKey these will be normalized for the mac
6556                 this.keyCode = e.keyCode;
6557                 // keyup warnings on firefox.
6558                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6559                 // cache the target for the delayed and or buffered events
6560                 this.target = E.getTarget(e);
6561                 // same for XY
6562                 this.xy = E.getXY(e);
6563             }else{
6564                 this.button = -1;
6565                 this.shiftKey = false;
6566                 this.ctrlKey = false;
6567                 this.altKey = false;
6568                 this.keyCode = 0;
6569                 this.charCode =0;
6570                 this.target = null;
6571                 this.xy = [0, 0];
6572             }
6573             return this;
6574         },
6575
6576         /**
6577          * Stop the event (preventDefault and stopPropagation)
6578          */
6579         stopEvent : function(){
6580             if(this.browserEvent){
6581                 if(this.browserEvent.type == 'mousedown'){
6582                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6583                 }
6584                 E.stopEvent(this.browserEvent);
6585             }
6586         },
6587
6588         /**
6589          * Prevents the browsers default handling of the event.
6590          */
6591         preventDefault : function(){
6592             if(this.browserEvent){
6593                 E.preventDefault(this.browserEvent);
6594             }
6595         },
6596
6597         /** @private */
6598         isNavKeyPress : function(){
6599             var k = this.keyCode;
6600             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6601             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6602         },
6603
6604         isSpecialKey : function(){
6605             var k = this.keyCode;
6606             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6607             (k == 16) || (k == 17) ||
6608             (k >= 18 && k <= 20) ||
6609             (k >= 33 && k <= 35) ||
6610             (k >= 36 && k <= 39) ||
6611             (k >= 44 && k <= 45);
6612         },
6613         /**
6614          * Cancels bubbling of the event.
6615          */
6616         stopPropagation : function(){
6617             if(this.browserEvent){
6618                 if(this.type == 'mousedown'){
6619                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6620                 }
6621                 E.stopPropagation(this.browserEvent);
6622             }
6623         },
6624
6625         /**
6626          * Gets the key code for the event.
6627          * @return {Number}
6628          */
6629         getCharCode : function(){
6630             return this.charCode || this.keyCode;
6631         },
6632
6633         /**
6634          * Returns a normalized keyCode for the event.
6635          * @return {Number} The key code
6636          */
6637         getKey : function(){
6638             var k = this.keyCode || this.charCode;
6639             return Roo.isSafari ? (safariKeys[k] || k) : k;
6640         },
6641
6642         /**
6643          * Gets the x coordinate of the event.
6644          * @return {Number}
6645          */
6646         getPageX : function(){
6647             return this.xy[0];
6648         },
6649
6650         /**
6651          * Gets the y coordinate of the event.
6652          * @return {Number}
6653          */
6654         getPageY : function(){
6655             return this.xy[1];
6656         },
6657
6658         /**
6659          * Gets the time of the event.
6660          * @return {Number}
6661          */
6662         getTime : function(){
6663             if(this.browserEvent){
6664                 return E.getTime(this.browserEvent);
6665             }
6666             return null;
6667         },
6668
6669         /**
6670          * Gets the page coordinates of the event.
6671          * @return {Array} The xy values like [x, y]
6672          */
6673         getXY : function(){
6674             return this.xy;
6675         },
6676
6677         /**
6678          * Gets the target for the event.
6679          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6680          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6681                 search as a number or element (defaults to 10 || document.body)
6682          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6683          * @return {HTMLelement}
6684          */
6685         getTarget : function(selector, maxDepth, returnEl){
6686             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6687         },
6688         /**
6689          * Gets the related target.
6690          * @return {HTMLElement}
6691          */
6692         getRelatedTarget : function(){
6693             if(this.browserEvent){
6694                 return E.getRelatedTarget(this.browserEvent);
6695             }
6696             return null;
6697         },
6698
6699         /**
6700          * Normalizes mouse wheel delta across browsers
6701          * @return {Number} The delta
6702          */
6703         getWheelDelta : function(){
6704             var e = this.browserEvent;
6705             var delta = 0;
6706             if(e.wheelDelta){ /* IE/Opera. */
6707                 delta = e.wheelDelta/120;
6708             }else if(e.detail){ /* Mozilla case. */
6709                 delta = -e.detail/3;
6710             }
6711             return delta;
6712         },
6713
6714         /**
6715          * Returns true if the control, meta, shift or alt key was pressed during this event.
6716          * @return {Boolean}
6717          */
6718         hasModifier : function(){
6719             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6720         },
6721
6722         /**
6723          * Returns true if the target of this event equals el or is a child of el
6724          * @param {String/HTMLElement/Element} el
6725          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6726          * @return {Boolean}
6727          */
6728         within : function(el, related){
6729             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6730             return t && Roo.fly(el).contains(t);
6731         },
6732
6733         getPoint : function(){
6734             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6735         }
6736     };
6737
6738     return new Roo.EventObjectImpl();
6739 }();
6740             
6741     /*
6742  * Based on:
6743  * Ext JS Library 1.1.1
6744  * Copyright(c) 2006-2007, Ext JS, LLC.
6745  *
6746  * Originally Released Under LGPL - original licence link has changed is not relivant.
6747  *
6748  * Fork - LGPL
6749  * <script type="text/javascript">
6750  */
6751
6752  
6753 // was in Composite Element!??!?!
6754  
6755 (function(){
6756     var D = Roo.lib.Dom;
6757     var E = Roo.lib.Event;
6758     var A = Roo.lib.Anim;
6759
6760     // local style camelizing for speed
6761     var propCache = {};
6762     var camelRe = /(-[a-z])/gi;
6763     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6764     var view = document.defaultView;
6765
6766 /**
6767  * @class Roo.Element
6768  * Represents an Element in the DOM.<br><br>
6769  * Usage:<br>
6770 <pre><code>
6771 var el = Roo.get("my-div");
6772
6773 // or with getEl
6774 var el = getEl("my-div");
6775
6776 // or with a DOM element
6777 var el = Roo.get(myDivElement);
6778 </code></pre>
6779  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6780  * each call instead of constructing a new one.<br><br>
6781  * <b>Animations</b><br />
6782  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6783  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6784 <pre>
6785 Option    Default   Description
6786 --------- --------  ---------------------------------------------
6787 duration  .35       The duration of the animation in seconds
6788 easing    easeOut   The YUI easing method
6789 callback  none      A function to execute when the anim completes
6790 scope     this      The scope (this) of the callback function
6791 </pre>
6792 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6793 * manipulate the animation. Here's an example:
6794 <pre><code>
6795 var el = Roo.get("my-div");
6796
6797 // no animation
6798 el.setWidth(100);
6799
6800 // default animation
6801 el.setWidth(100, true);
6802
6803 // animation with some options set
6804 el.setWidth(100, {
6805     duration: 1,
6806     callback: this.foo,
6807     scope: this
6808 });
6809
6810 // using the "anim" property to get the Anim object
6811 var opt = {
6812     duration: 1,
6813     callback: this.foo,
6814     scope: this
6815 };
6816 el.setWidth(100, opt);
6817 ...
6818 if(opt.anim.isAnimated()){
6819     opt.anim.stop();
6820 }
6821 </code></pre>
6822 * <b> Composite (Collections of) Elements</b><br />
6823  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6824  * @constructor Create a new Element directly.
6825  * @param {String/HTMLElement} element
6826  * @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).
6827  */
6828     Roo.Element = function(element, forceNew){
6829         var dom = typeof element == "string" ?
6830                 document.getElementById(element) : element;
6831         if(!dom){ // invalid id/element
6832             return null;
6833         }
6834         var id = dom.id;
6835         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6836             return Roo.Element.cache[id];
6837         }
6838
6839         /**
6840          * The DOM element
6841          * @type HTMLElement
6842          */
6843         this.dom = dom;
6844
6845         /**
6846          * The DOM element ID
6847          * @type String
6848          */
6849         this.id = id || Roo.id(dom);
6850     };
6851
6852     var El = Roo.Element;
6853
6854     El.prototype = {
6855         /**
6856          * The element's default display mode  (defaults to "")
6857          * @type String
6858          */
6859         originalDisplay : "",
6860
6861         visibilityMode : 1,
6862         /**
6863          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6864          * @type String
6865          */
6866         defaultUnit : "px",
6867         /**
6868          * Sets the element's visibility mode. When setVisible() is called it
6869          * will use this to determine whether to set the visibility or the display property.
6870          * @param visMode Element.VISIBILITY or Element.DISPLAY
6871          * @return {Roo.Element} this
6872          */
6873         setVisibilityMode : function(visMode){
6874             this.visibilityMode = visMode;
6875             return this;
6876         },
6877         /**
6878          * Convenience method for setVisibilityMode(Element.DISPLAY)
6879          * @param {String} display (optional) What to set display to when visible
6880          * @return {Roo.Element} this
6881          */
6882         enableDisplayMode : function(display){
6883             this.setVisibilityMode(El.DISPLAY);
6884             if(typeof display != "undefined") this.originalDisplay = display;
6885             return this;
6886         },
6887
6888         /**
6889          * 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)
6890          * @param {String} selector The simple selector to test
6891          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6892                 search as a number or element (defaults to 10 || document.body)
6893          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6894          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6895          */
6896         findParent : function(simpleSelector, maxDepth, returnEl){
6897             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6898             maxDepth = maxDepth || 50;
6899             if(typeof maxDepth != "number"){
6900                 stopEl = Roo.getDom(maxDepth);
6901                 maxDepth = 10;
6902             }
6903             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6904                 if(dq.is(p, simpleSelector)){
6905                     return returnEl ? Roo.get(p) : p;
6906                 }
6907                 depth++;
6908                 p = p.parentNode;
6909             }
6910             return null;
6911         },
6912
6913
6914         /**
6915          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6916          * @param {String} selector The simple selector to test
6917          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6918                 search as a number or element (defaults to 10 || document.body)
6919          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6920          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6921          */
6922         findParentNode : function(simpleSelector, maxDepth, returnEl){
6923             var p = Roo.fly(this.dom.parentNode, '_internal');
6924             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6925         },
6926
6927         /**
6928          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6929          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6930          * @param {String} selector The simple selector to test
6931          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6932                 search as a number or element (defaults to 10 || document.body)
6933          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6934          */
6935         up : function(simpleSelector, maxDepth){
6936             return this.findParentNode(simpleSelector, maxDepth, true);
6937         },
6938
6939
6940
6941         /**
6942          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6943          * @param {String} selector The simple selector to test
6944          * @return {Boolean} True if this element matches the selector, else false
6945          */
6946         is : function(simpleSelector){
6947             return Roo.DomQuery.is(this.dom, simpleSelector);
6948         },
6949
6950         /**
6951          * Perform animation on this element.
6952          * @param {Object} args The YUI animation control args
6953          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6954          * @param {Function} onComplete (optional) Function to call when animation completes
6955          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6956          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6957          * @return {Roo.Element} this
6958          */
6959         animate : function(args, duration, onComplete, easing, animType){
6960             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6961             return this;
6962         },
6963
6964         /*
6965          * @private Internal animation call
6966          */
6967         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6968             animType = animType || 'run';
6969             opt = opt || {};
6970             var anim = Roo.lib.Anim[animType](
6971                 this.dom, args,
6972                 (opt.duration || defaultDur) || .35,
6973                 (opt.easing || defaultEase) || 'easeOut',
6974                 function(){
6975                     Roo.callback(cb, this);
6976                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6977                 },
6978                 this
6979             );
6980             opt.anim = anim;
6981             return anim;
6982         },
6983
6984         // private legacy anim prep
6985         preanim : function(a, i){
6986             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
6987         },
6988
6989         /**
6990          * Removes worthless text nodes
6991          * @param {Boolean} forceReclean (optional) By default the element
6992          * keeps track if it has been cleaned already so
6993          * you can call this over and over. However, if you update the element and
6994          * need to force a reclean, you can pass true.
6995          */
6996         clean : function(forceReclean){
6997             if(this.isCleaned && forceReclean !== true){
6998                 return this;
6999             }
7000             var ns = /\S/;
7001             var d = this.dom, n = d.firstChild, ni = -1;
7002             while(n){
7003                 var nx = n.nextSibling;
7004                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7005                     d.removeChild(n);
7006                 }else{
7007                     n.nodeIndex = ++ni;
7008                 }
7009                 n = nx;
7010             }
7011             this.isCleaned = true;
7012             return this;
7013         },
7014
7015         // private
7016         calcOffsetsTo : function(el){
7017             el = Roo.get(el);
7018             var d = el.dom;
7019             var restorePos = false;
7020             if(el.getStyle('position') == 'static'){
7021                 el.position('relative');
7022                 restorePos = true;
7023             }
7024             var x = 0, y =0;
7025             var op = this.dom;
7026             while(op && op != d && op.tagName != 'HTML'){
7027                 x+= op.offsetLeft;
7028                 y+= op.offsetTop;
7029                 op = op.offsetParent;
7030             }
7031             if(restorePos){
7032                 el.position('static');
7033             }
7034             return [x, y];
7035         },
7036
7037         /**
7038          * Scrolls this element into view within the passed container.
7039          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7040          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7041          * @return {Roo.Element} this
7042          */
7043         scrollIntoView : function(container, hscroll){
7044             var c = Roo.getDom(container) || document.body;
7045             var el = this.dom;
7046
7047             var o = this.calcOffsetsTo(c),
7048                 l = o[0],
7049                 t = o[1],
7050                 b = t+el.offsetHeight,
7051                 r = l+el.offsetWidth;
7052
7053             var ch = c.clientHeight;
7054             var ct = parseInt(c.scrollTop, 10);
7055             var cl = parseInt(c.scrollLeft, 10);
7056             var cb = ct + ch;
7057             var cr = cl + c.clientWidth;
7058
7059             if(t < ct){
7060                 c.scrollTop = t;
7061             }else if(b > cb){
7062                 c.scrollTop = b-ch;
7063             }
7064
7065             if(hscroll !== false){
7066                 if(l < cl){
7067                     c.scrollLeft = l;
7068                 }else if(r > cr){
7069                     c.scrollLeft = r-c.clientWidth;
7070                 }
7071             }
7072             return this;
7073         },
7074
7075         // private
7076         scrollChildIntoView : function(child, hscroll){
7077             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7078         },
7079
7080         /**
7081          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7082          * the new height may not be available immediately.
7083          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7084          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7085          * @param {Function} onComplete (optional) Function to call when animation completes
7086          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7087          * @return {Roo.Element} this
7088          */
7089         autoHeight : function(animate, duration, onComplete, easing){
7090             var oldHeight = this.getHeight();
7091             this.clip();
7092             this.setHeight(1); // force clipping
7093             setTimeout(function(){
7094                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7095                 if(!animate){
7096                     this.setHeight(height);
7097                     this.unclip();
7098                     if(typeof onComplete == "function"){
7099                         onComplete();
7100                     }
7101                 }else{
7102                     this.setHeight(oldHeight); // restore original height
7103                     this.setHeight(height, animate, duration, function(){
7104                         this.unclip();
7105                         if(typeof onComplete == "function") onComplete();
7106                     }.createDelegate(this), easing);
7107                 }
7108             }.createDelegate(this), 0);
7109             return this;
7110         },
7111
7112         /**
7113          * Returns true if this element is an ancestor of the passed element
7114          * @param {HTMLElement/String} el The element to check
7115          * @return {Boolean} True if this element is an ancestor of el, else false
7116          */
7117         contains : function(el){
7118             if(!el){return false;}
7119             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7120         },
7121
7122         /**
7123          * Checks whether the element is currently visible using both visibility and display properties.
7124          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7125          * @return {Boolean} True if the element is currently visible, else false
7126          */
7127         isVisible : function(deep) {
7128             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7129             if(deep !== true || !vis){
7130                 return vis;
7131             }
7132             var p = this.dom.parentNode;
7133             while(p && p.tagName.toLowerCase() != "body"){
7134                 if(!Roo.fly(p, '_isVisible').isVisible()){
7135                     return false;
7136                 }
7137                 p = p.parentNode;
7138             }
7139             return true;
7140         },
7141
7142         /**
7143          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7144          * @param {String} selector The CSS selector
7145          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7146          * @return {CompositeElement/CompositeElementLite} The composite element
7147          */
7148         select : function(selector, unique){
7149             return El.select(selector, unique, this.dom);
7150         },
7151
7152         /**
7153          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7154          * @param {String} selector The CSS selector
7155          * @return {Array} An array of the matched nodes
7156          */
7157         query : function(selector, unique){
7158             return Roo.DomQuery.select(selector, this.dom);
7159         },
7160
7161         /**
7162          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7163          * @param {String} selector The CSS selector
7164          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7165          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7166          */
7167         child : function(selector, returnDom){
7168             var n = Roo.DomQuery.selectNode(selector, this.dom);
7169             return returnDom ? n : Roo.get(n);
7170         },
7171
7172         /**
7173          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7174          * @param {String} selector The CSS selector
7175          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7176          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7177          */
7178         down : function(selector, returnDom){
7179             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7180             return returnDom ? n : Roo.get(n);
7181         },
7182
7183         /**
7184          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7185          * @param {String} group The group the DD object is member of
7186          * @param {Object} config The DD config object
7187          * @param {Object} overrides An object containing methods to override/implement on the DD object
7188          * @return {Roo.dd.DD} The DD object
7189          */
7190         initDD : function(group, config, overrides){
7191             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7192             return Roo.apply(dd, overrides);
7193         },
7194
7195         /**
7196          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7197          * @param {String} group The group the DDProxy object is member of
7198          * @param {Object} config The DDProxy config object
7199          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7200          * @return {Roo.dd.DDProxy} The DDProxy object
7201          */
7202         initDDProxy : function(group, config, overrides){
7203             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7204             return Roo.apply(dd, overrides);
7205         },
7206
7207         /**
7208          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7209          * @param {String} group The group the DDTarget object is member of
7210          * @param {Object} config The DDTarget config object
7211          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7212          * @return {Roo.dd.DDTarget} The DDTarget object
7213          */
7214         initDDTarget : function(group, config, overrides){
7215             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7216             return Roo.apply(dd, overrides);
7217         },
7218
7219         /**
7220          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7221          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7222          * @param {Boolean} visible Whether the element is visible
7223          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7224          * @return {Roo.Element} this
7225          */
7226          setVisible : function(visible, animate){
7227             if(!animate || !A){
7228                 if(this.visibilityMode == El.DISPLAY){
7229                     this.setDisplayed(visible);
7230                 }else{
7231                     this.fixDisplay();
7232                     this.dom.style.visibility = visible ? "visible" : "hidden";
7233                 }
7234             }else{
7235                 // closure for composites
7236                 var dom = this.dom;
7237                 var visMode = this.visibilityMode;
7238                 if(visible){
7239                     this.setOpacity(.01);
7240                     this.setVisible(true);
7241                 }
7242                 this.anim({opacity: { to: (visible?1:0) }},
7243                       this.preanim(arguments, 1),
7244                       null, .35, 'easeIn', function(){
7245                          if(!visible){
7246                              if(visMode == El.DISPLAY){
7247                                  dom.style.display = "none";
7248                              }else{
7249                                  dom.style.visibility = "hidden";
7250                              }
7251                              Roo.get(dom).setOpacity(1);
7252                          }
7253                      });
7254             }
7255             return this;
7256         },
7257
7258         /**
7259          * Returns true if display is not "none"
7260          * @return {Boolean}
7261          */
7262         isDisplayed : function() {
7263             return this.getStyle("display") != "none";
7264         },
7265
7266         /**
7267          * Toggles the element's visibility or display, depending on visibility mode.
7268          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7269          * @return {Roo.Element} this
7270          */
7271         toggle : function(animate){
7272             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7273             return this;
7274         },
7275
7276         /**
7277          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7278          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7279          * @return {Roo.Element} this
7280          */
7281         setDisplayed : function(value) {
7282             if(typeof value == "boolean"){
7283                value = value ? this.originalDisplay : "none";
7284             }
7285             this.setStyle("display", value);
7286             return this;
7287         },
7288
7289         /**
7290          * Tries to focus the element. Any exceptions are caught and ignored.
7291          * @return {Roo.Element} this
7292          */
7293         focus : function() {
7294             try{
7295                 this.dom.focus();
7296             }catch(e){}
7297             return this;
7298         },
7299
7300         /**
7301          * Tries to blur the element. Any exceptions are caught and ignored.
7302          * @return {Roo.Element} this
7303          */
7304         blur : function() {
7305             try{
7306                 this.dom.blur();
7307             }catch(e){}
7308             return this;
7309         },
7310
7311         /**
7312          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7313          * @param {String/Array} className The CSS class to add, or an array of classes
7314          * @return {Roo.Element} this
7315          */
7316         addClass : function(className){
7317             if(className instanceof Array){
7318                 for(var i = 0, len = className.length; i < len; i++) {
7319                     this.addClass(className[i]);
7320                 }
7321             }else{
7322                 if(className && !this.hasClass(className)){
7323                     this.dom.className = this.dom.className + " " + className;
7324                 }
7325             }
7326             return this;
7327         },
7328
7329         /**
7330          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7331          * @param {String/Array} className The CSS class to add, or an array of classes
7332          * @return {Roo.Element} this
7333          */
7334         radioClass : function(className){
7335             var siblings = this.dom.parentNode.childNodes;
7336             for(var i = 0; i < siblings.length; i++) {
7337                 var s = siblings[i];
7338                 if(s.nodeType == 1){
7339                     Roo.get(s).removeClass(className);
7340                 }
7341             }
7342             this.addClass(className);
7343             return this;
7344         },
7345
7346         /**
7347          * Removes one or more CSS classes from the element.
7348          * @param {String/Array} className The CSS class to remove, or an array of classes
7349          * @return {Roo.Element} this
7350          */
7351         removeClass : function(className){
7352             if(!className || !this.dom.className){
7353                 return this;
7354             }
7355             if(className instanceof Array){
7356                 for(var i = 0, len = className.length; i < len; i++) {
7357                     this.removeClass(className[i]);
7358                 }
7359             }else{
7360                 if(this.hasClass(className)){
7361                     var re = this.classReCache[className];
7362                     if (!re) {
7363                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7364                        this.classReCache[className] = re;
7365                     }
7366                     this.dom.className =
7367                         this.dom.className.replace(re, " ");
7368                 }
7369             }
7370             return this;
7371         },
7372
7373         // private
7374         classReCache: {},
7375
7376         /**
7377          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7378          * @param {String} className The CSS class to toggle
7379          * @return {Roo.Element} this
7380          */
7381         toggleClass : function(className){
7382             if(this.hasClass(className)){
7383                 this.removeClass(className);
7384             }else{
7385                 this.addClass(className);
7386             }
7387             return this;
7388         },
7389
7390         /**
7391          * Checks if the specified CSS class exists on this element's DOM node.
7392          * @param {String} className The CSS class to check for
7393          * @return {Boolean} True if the class exists, else false
7394          */
7395         hasClass : function(className){
7396             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7397         },
7398
7399         /**
7400          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7401          * @param {String} oldClassName The CSS class to replace
7402          * @param {String} newClassName The replacement CSS class
7403          * @return {Roo.Element} this
7404          */
7405         replaceClass : function(oldClassName, newClassName){
7406             this.removeClass(oldClassName);
7407             this.addClass(newClassName);
7408             return this;
7409         },
7410
7411         /**
7412          * Returns an object with properties matching the styles requested.
7413          * For example, el.getStyles('color', 'font-size', 'width') might return
7414          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7415          * @param {String} style1 A style name
7416          * @param {String} style2 A style name
7417          * @param {String} etc.
7418          * @return {Object} The style object
7419          */
7420         getStyles : function(){
7421             var a = arguments, len = a.length, r = {};
7422             for(var i = 0; i < len; i++){
7423                 r[a[i]] = this.getStyle(a[i]);
7424             }
7425             return r;
7426         },
7427
7428         /**
7429          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7430          * @param {String} property The style property whose value is returned.
7431          * @return {String} The current value of the style property for this element.
7432          */
7433         getStyle : function(){
7434             return view && view.getComputedStyle ?
7435                 function(prop){
7436                     var el = this.dom, v, cs, camel;
7437                     if(prop == 'float'){
7438                         prop = "cssFloat";
7439                     }
7440                     if(el.style && (v = el.style[prop])){
7441                         return v;
7442                     }
7443                     if(cs = view.getComputedStyle(el, "")){
7444                         if(!(camel = propCache[prop])){
7445                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7446                         }
7447                         return cs[camel];
7448                     }
7449                     return null;
7450                 } :
7451                 function(prop){
7452                     var el = this.dom, v, cs, camel;
7453                     if(prop == 'opacity'){
7454                         if(typeof el.style.filter == 'string'){
7455                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7456                             if(m){
7457                                 var fv = parseFloat(m[1]);
7458                                 if(!isNaN(fv)){
7459                                     return fv ? fv / 100 : 0;
7460                                 }
7461                             }
7462                         }
7463                         return 1;
7464                     }else if(prop == 'float'){
7465                         prop = "styleFloat";
7466                     }
7467                     if(!(camel = propCache[prop])){
7468                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7469                     }
7470                     if(v = el.style[camel]){
7471                         return v;
7472                     }
7473                     if(cs = el.currentStyle){
7474                         return cs[camel];
7475                     }
7476                     return null;
7477                 };
7478         }(),
7479
7480         /**
7481          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7482          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7483          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7484          * @return {Roo.Element} this
7485          */
7486         setStyle : function(prop, value){
7487             if(typeof prop == "string"){
7488                 
7489                 if (prop == 'float') {
7490                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7491                     return this;
7492                 }
7493                 
7494                 var camel;
7495                 if(!(camel = propCache[prop])){
7496                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7497                 }
7498                 
7499                 if(camel == 'opacity') {
7500                     this.setOpacity(value);
7501                 }else{
7502                     this.dom.style[camel] = value;
7503                 }
7504             }else{
7505                 for(var style in prop){
7506                     if(typeof prop[style] != "function"){
7507                        this.setStyle(style, prop[style]);
7508                     }
7509                 }
7510             }
7511             return this;
7512         },
7513
7514         /**
7515          * More flexible version of {@link #setStyle} for setting style properties.
7516          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7517          * a function which returns such a specification.
7518          * @return {Roo.Element} this
7519          */
7520         applyStyles : function(style){
7521             Roo.DomHelper.applyStyles(this.dom, style);
7522             return this;
7523         },
7524
7525         /**
7526           * 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).
7527           * @return {Number} The X position of the element
7528           */
7529         getX : function(){
7530             return D.getX(this.dom);
7531         },
7532
7533         /**
7534           * 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).
7535           * @return {Number} The Y position of the element
7536           */
7537         getY : function(){
7538             return D.getY(this.dom);
7539         },
7540
7541         /**
7542           * 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).
7543           * @return {Array} The XY position of the element
7544           */
7545         getXY : function(){
7546             return D.getXY(this.dom);
7547         },
7548
7549         /**
7550          * 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).
7551          * @param {Number} The X position of the element
7552          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7553          * @return {Roo.Element} this
7554          */
7555         setX : function(x, animate){
7556             if(!animate || !A){
7557                 D.setX(this.dom, x);
7558             }else{
7559                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7560             }
7561             return this;
7562         },
7563
7564         /**
7565          * 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).
7566          * @param {Number} The Y position of the element
7567          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7568          * @return {Roo.Element} this
7569          */
7570         setY : function(y, animate){
7571             if(!animate || !A){
7572                 D.setY(this.dom, y);
7573             }else{
7574                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7575             }
7576             return this;
7577         },
7578
7579         /**
7580          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7581          * @param {String} left The left CSS property value
7582          * @return {Roo.Element} this
7583          */
7584         setLeft : function(left){
7585             this.setStyle("left", this.addUnits(left));
7586             return this;
7587         },
7588
7589         /**
7590          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7591          * @param {String} top The top CSS property value
7592          * @return {Roo.Element} this
7593          */
7594         setTop : function(top){
7595             this.setStyle("top", this.addUnits(top));
7596             return this;
7597         },
7598
7599         /**
7600          * Sets the element's CSS right style.
7601          * @param {String} right The right CSS property value
7602          * @return {Roo.Element} this
7603          */
7604         setRight : function(right){
7605             this.setStyle("right", this.addUnits(right));
7606             return this;
7607         },
7608
7609         /**
7610          * Sets the element's CSS bottom style.
7611          * @param {String} bottom The bottom CSS property value
7612          * @return {Roo.Element} this
7613          */
7614         setBottom : function(bottom){
7615             this.setStyle("bottom", this.addUnits(bottom));
7616             return this;
7617         },
7618
7619         /**
7620          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7621          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7622          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7623          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7624          * @return {Roo.Element} this
7625          */
7626         setXY : function(pos, animate){
7627             if(!animate || !A){
7628                 D.setXY(this.dom, pos);
7629             }else{
7630                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7631             }
7632             return this;
7633         },
7634
7635         /**
7636          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7637          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7638          * @param {Number} x X value for new position (coordinates are page-based)
7639          * @param {Number} y Y value for new position (coordinates are page-based)
7640          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7641          * @return {Roo.Element} this
7642          */
7643         setLocation : function(x, y, animate){
7644             this.setXY([x, y], this.preanim(arguments, 2));
7645             return this;
7646         },
7647
7648         /**
7649          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7650          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7651          * @param {Number} x X value for new position (coordinates are page-based)
7652          * @param {Number} y Y value for new position (coordinates are page-based)
7653          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7654          * @return {Roo.Element} this
7655          */
7656         moveTo : function(x, y, animate){
7657             this.setXY([x, y], this.preanim(arguments, 2));
7658             return this;
7659         },
7660
7661         /**
7662          * Returns the region of the given element.
7663          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7664          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7665          */
7666         getRegion : function(){
7667             return D.getRegion(this.dom);
7668         },
7669
7670         /**
7671          * Returns the offset height of the element
7672          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7673          * @return {Number} The element's height
7674          */
7675         getHeight : function(contentHeight){
7676             var h = this.dom.offsetHeight || 0;
7677             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7678         },
7679
7680         /**
7681          * Returns the offset width of the element
7682          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7683          * @return {Number} The element's width
7684          */
7685         getWidth : function(contentWidth){
7686             var w = this.dom.offsetWidth || 0;
7687             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7688         },
7689
7690         /**
7691          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7692          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7693          * if a height has not been set using CSS.
7694          * @return {Number}
7695          */
7696         getComputedHeight : function(){
7697             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7698             if(!h){
7699                 h = parseInt(this.getStyle('height'), 10) || 0;
7700                 if(!this.isBorderBox()){
7701                     h += this.getFrameWidth('tb');
7702                 }
7703             }
7704             return h;
7705         },
7706
7707         /**
7708          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7709          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7710          * if a width has not been set using CSS.
7711          * @return {Number}
7712          */
7713         getComputedWidth : function(){
7714             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7715             if(!w){
7716                 w = parseInt(this.getStyle('width'), 10) || 0;
7717                 if(!this.isBorderBox()){
7718                     w += this.getFrameWidth('lr');
7719                 }
7720             }
7721             return w;
7722         },
7723
7724         /**
7725          * Returns the size of the element.
7726          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7727          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7728          */
7729         getSize : function(contentSize){
7730             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7731         },
7732
7733         /**
7734          * Returns the width and height of the viewport.
7735          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7736          */
7737         getViewSize : function(){
7738             var d = this.dom, doc = document, aw = 0, ah = 0;
7739             if(d == doc || d == doc.body){
7740                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7741             }else{
7742                 return {
7743                     width : d.clientWidth,
7744                     height: d.clientHeight
7745                 };
7746             }
7747         },
7748
7749         /**
7750          * Returns the value of the "value" attribute
7751          * @param {Boolean} asNumber true to parse the value as a number
7752          * @return {String/Number}
7753          */
7754         getValue : function(asNumber){
7755             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7756         },
7757
7758         // private
7759         adjustWidth : function(width){
7760             if(typeof width == "number"){
7761                 if(this.autoBoxAdjust && !this.isBorderBox()){
7762                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7763                 }
7764                 if(width < 0){
7765                     width = 0;
7766                 }
7767             }
7768             return width;
7769         },
7770
7771         // private
7772         adjustHeight : function(height){
7773             if(typeof height == "number"){
7774                if(this.autoBoxAdjust && !this.isBorderBox()){
7775                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7776                }
7777                if(height < 0){
7778                    height = 0;
7779                }
7780             }
7781             return height;
7782         },
7783
7784         /**
7785          * Set the width of the element
7786          * @param {Number} width The new width
7787          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7788          * @return {Roo.Element} this
7789          */
7790         setWidth : function(width, animate){
7791             width = this.adjustWidth(width);
7792             if(!animate || !A){
7793                 this.dom.style.width = this.addUnits(width);
7794             }else{
7795                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7796             }
7797             return this;
7798         },
7799
7800         /**
7801          * Set the height of the element
7802          * @param {Number} height The new height
7803          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7804          * @return {Roo.Element} this
7805          */
7806          setHeight : function(height, animate){
7807             height = this.adjustHeight(height);
7808             if(!animate || !A){
7809                 this.dom.style.height = this.addUnits(height);
7810             }else{
7811                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7812             }
7813             return this;
7814         },
7815
7816         /**
7817          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7818          * @param {Number} width The new width
7819          * @param {Number} height The new height
7820          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7821          * @return {Roo.Element} this
7822          */
7823          setSize : function(width, height, animate){
7824             if(typeof width == "object"){ // in case of object from getSize()
7825                 height = width.height; width = width.width;
7826             }
7827             width = this.adjustWidth(width); height = this.adjustHeight(height);
7828             if(!animate || !A){
7829                 this.dom.style.width = this.addUnits(width);
7830                 this.dom.style.height = this.addUnits(height);
7831             }else{
7832                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7833             }
7834             return this;
7835         },
7836
7837         /**
7838          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7839          * @param {Number} x X value for new position (coordinates are page-based)
7840          * @param {Number} y Y value for new position (coordinates are page-based)
7841          * @param {Number} width The new width
7842          * @param {Number} height The new height
7843          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7844          * @return {Roo.Element} this
7845          */
7846         setBounds : function(x, y, width, height, animate){
7847             if(!animate || !A){
7848                 this.setSize(width, height);
7849                 this.setLocation(x, y);
7850             }else{
7851                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7852                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7853                               this.preanim(arguments, 4), 'motion');
7854             }
7855             return this;
7856         },
7857
7858         /**
7859          * 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.
7860          * @param {Roo.lib.Region} region The region to fill
7861          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7862          * @return {Roo.Element} this
7863          */
7864         setRegion : function(region, animate){
7865             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7866             return this;
7867         },
7868
7869         /**
7870          * Appends an event handler
7871          *
7872          * @param {String}   eventName     The type of event to append
7873          * @param {Function} fn        The method the event invokes
7874          * @param {Object} scope       (optional) The scope (this object) of the fn
7875          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7876          */
7877         addListener : function(eventName, fn, scope, options){
7878             if (this.dom) {
7879                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7880             }
7881         },
7882
7883         /**
7884          * Removes an event handler from this element
7885          * @param {String} eventName the type of event to remove
7886          * @param {Function} fn the method the event invokes
7887          * @return {Roo.Element} this
7888          */
7889         removeListener : function(eventName, fn){
7890             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7891             return this;
7892         },
7893
7894         /**
7895          * Removes all previous added listeners from this element
7896          * @return {Roo.Element} this
7897          */
7898         removeAllListeners : function(){
7899             E.purgeElement(this.dom);
7900             return this;
7901         },
7902
7903         relayEvent : function(eventName, observable){
7904             this.on(eventName, function(e){
7905                 observable.fireEvent(eventName, e);
7906             });
7907         },
7908
7909         /**
7910          * Set the opacity of the element
7911          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7912          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7913          * @return {Roo.Element} this
7914          */
7915          setOpacity : function(opacity, animate){
7916             if(!animate || !A){
7917                 var s = this.dom.style;
7918                 if(Roo.isIE){
7919                     s.zoom = 1;
7920                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7921                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7922                 }else{
7923                     s.opacity = opacity;
7924                 }
7925             }else{
7926                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7927             }
7928             return this;
7929         },
7930
7931         /**
7932          * Gets the left X coordinate
7933          * @param {Boolean} local True to get the local css position instead of page coordinate
7934          * @return {Number}
7935          */
7936         getLeft : function(local){
7937             if(!local){
7938                 return this.getX();
7939             }else{
7940                 return parseInt(this.getStyle("left"), 10) || 0;
7941             }
7942         },
7943
7944         /**
7945          * Gets the right X coordinate of the element (element X position + element width)
7946          * @param {Boolean} local True to get the local css position instead of page coordinate
7947          * @return {Number}
7948          */
7949         getRight : function(local){
7950             if(!local){
7951                 return this.getX() + this.getWidth();
7952             }else{
7953                 return (this.getLeft(true) + this.getWidth()) || 0;
7954             }
7955         },
7956
7957         /**
7958          * Gets the top Y coordinate
7959          * @param {Boolean} local True to get the local css position instead of page coordinate
7960          * @return {Number}
7961          */
7962         getTop : function(local) {
7963             if(!local){
7964                 return this.getY();
7965             }else{
7966                 return parseInt(this.getStyle("top"), 10) || 0;
7967             }
7968         },
7969
7970         /**
7971          * Gets the bottom Y coordinate of the element (element Y position + element height)
7972          * @param {Boolean} local True to get the local css position instead of page coordinate
7973          * @return {Number}
7974          */
7975         getBottom : function(local){
7976             if(!local){
7977                 return this.getY() + this.getHeight();
7978             }else{
7979                 return (this.getTop(true) + this.getHeight()) || 0;
7980             }
7981         },
7982
7983         /**
7984         * Initializes positioning on this element. If a desired position is not passed, it will make the
7985         * the element positioned relative IF it is not already positioned.
7986         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
7987         * @param {Number} zIndex (optional) The zIndex to apply
7988         * @param {Number} x (optional) Set the page X position
7989         * @param {Number} y (optional) Set the page Y position
7990         */
7991         position : function(pos, zIndex, x, y){
7992             if(!pos){
7993                if(this.getStyle('position') == 'static'){
7994                    this.setStyle('position', 'relative');
7995                }
7996             }else{
7997                 this.setStyle("position", pos);
7998             }
7999             if(zIndex){
8000                 this.setStyle("z-index", zIndex);
8001             }
8002             if(x !== undefined && y !== undefined){
8003                 this.setXY([x, y]);
8004             }else if(x !== undefined){
8005                 this.setX(x);
8006             }else if(y !== undefined){
8007                 this.setY(y);
8008             }
8009         },
8010
8011         /**
8012         * Clear positioning back to the default when the document was loaded
8013         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8014         * @return {Roo.Element} this
8015          */
8016         clearPositioning : function(value){
8017             value = value ||'';
8018             this.setStyle({
8019                 "left": value,
8020                 "right": value,
8021                 "top": value,
8022                 "bottom": value,
8023                 "z-index": "",
8024                 "position" : "static"
8025             });
8026             return this;
8027         },
8028
8029         /**
8030         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8031         * snapshot before performing an update and then restoring the element.
8032         * @return {Object}
8033         */
8034         getPositioning : function(){
8035             var l = this.getStyle("left");
8036             var t = this.getStyle("top");
8037             return {
8038                 "position" : this.getStyle("position"),
8039                 "left" : l,
8040                 "right" : l ? "" : this.getStyle("right"),
8041                 "top" : t,
8042                 "bottom" : t ? "" : this.getStyle("bottom"),
8043                 "z-index" : this.getStyle("z-index")
8044             };
8045         },
8046
8047         /**
8048          * Gets the width of the border(s) for the specified side(s)
8049          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8050          * passing lr would get the border (l)eft width + the border (r)ight width.
8051          * @return {Number} The width of the sides passed added together
8052          */
8053         getBorderWidth : function(side){
8054             return this.addStyles(side, El.borders);
8055         },
8056
8057         /**
8058          * Gets the width of the padding(s) for the specified side(s)
8059          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8060          * passing lr would get the padding (l)eft + the padding (r)ight.
8061          * @return {Number} The padding of the sides passed added together
8062          */
8063         getPadding : function(side){
8064             return this.addStyles(side, El.paddings);
8065         },
8066
8067         /**
8068         * Set positioning with an object returned by getPositioning().
8069         * @param {Object} posCfg
8070         * @return {Roo.Element} this
8071          */
8072         setPositioning : function(pc){
8073             this.applyStyles(pc);
8074             if(pc.right == "auto"){
8075                 this.dom.style.right = "";
8076             }
8077             if(pc.bottom == "auto"){
8078                 this.dom.style.bottom = "";
8079             }
8080             return this;
8081         },
8082
8083         // private
8084         fixDisplay : function(){
8085             if(this.getStyle("display") == "none"){
8086                 this.setStyle("visibility", "hidden");
8087                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8088                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8089                     this.setStyle("display", "block");
8090                 }
8091             }
8092         },
8093
8094         /**
8095          * Quick set left and top adding default units
8096          * @param {String} left The left CSS property value
8097          * @param {String} top The top CSS property value
8098          * @return {Roo.Element} this
8099          */
8100          setLeftTop : function(left, top){
8101             this.dom.style.left = this.addUnits(left);
8102             this.dom.style.top = this.addUnits(top);
8103             return this;
8104         },
8105
8106         /**
8107          * Move this element relative to its current position.
8108          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8109          * @param {Number} distance How far to move the element in pixels
8110          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8111          * @return {Roo.Element} this
8112          */
8113          move : function(direction, distance, animate){
8114             var xy = this.getXY();
8115             direction = direction.toLowerCase();
8116             switch(direction){
8117                 case "l":
8118                 case "left":
8119                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8120                     break;
8121                case "r":
8122                case "right":
8123                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8124                     break;
8125                case "t":
8126                case "top":
8127                case "up":
8128                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8129                     break;
8130                case "b":
8131                case "bottom":
8132                case "down":
8133                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8134                     break;
8135             }
8136             return this;
8137         },
8138
8139         /**
8140          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8141          * @return {Roo.Element} this
8142          */
8143         clip : function(){
8144             if(!this.isClipped){
8145                this.isClipped = true;
8146                this.originalClip = {
8147                    "o": this.getStyle("overflow"),
8148                    "x": this.getStyle("overflow-x"),
8149                    "y": this.getStyle("overflow-y")
8150                };
8151                this.setStyle("overflow", "hidden");
8152                this.setStyle("overflow-x", "hidden");
8153                this.setStyle("overflow-y", "hidden");
8154             }
8155             return this;
8156         },
8157
8158         /**
8159          *  Return clipping (overflow) to original clipping before clip() was called
8160          * @return {Roo.Element} this
8161          */
8162         unclip : function(){
8163             if(this.isClipped){
8164                 this.isClipped = false;
8165                 var o = this.originalClip;
8166                 if(o.o){this.setStyle("overflow", o.o);}
8167                 if(o.x){this.setStyle("overflow-x", o.x);}
8168                 if(o.y){this.setStyle("overflow-y", o.y);}
8169             }
8170             return this;
8171         },
8172
8173
8174         /**
8175          * Gets the x,y coordinates specified by the anchor position on the element.
8176          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8177          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8178          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8179          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8180          * @return {Array} [x, y] An array containing the element's x and y coordinates
8181          */
8182         getAnchorXY : function(anchor, local, s){
8183             //Passing a different size is useful for pre-calculating anchors,
8184             //especially for anchored animations that change the el size.
8185
8186             var w, h, vp = false;
8187             if(!s){
8188                 var d = this.dom;
8189                 if(d == document.body || d == document){
8190                     vp = true;
8191                     w = D.getViewWidth(); h = D.getViewHeight();
8192                 }else{
8193                     w = this.getWidth(); h = this.getHeight();
8194                 }
8195             }else{
8196                 w = s.width;  h = s.height;
8197             }
8198             var x = 0, y = 0, r = Math.round;
8199             switch((anchor || "tl").toLowerCase()){
8200                 case "c":
8201                     x = r(w*.5);
8202                     y = r(h*.5);
8203                 break;
8204                 case "t":
8205                     x = r(w*.5);
8206                     y = 0;
8207                 break;
8208                 case "l":
8209                     x = 0;
8210                     y = r(h*.5);
8211                 break;
8212                 case "r":
8213                     x = w;
8214                     y = r(h*.5);
8215                 break;
8216                 case "b":
8217                     x = r(w*.5);
8218                     y = h;
8219                 break;
8220                 case "tl":
8221                     x = 0;
8222                     y = 0;
8223                 break;
8224                 case "bl":
8225                     x = 0;
8226                     y = h;
8227                 break;
8228                 case "br":
8229                     x = w;
8230                     y = h;
8231                 break;
8232                 case "tr":
8233                     x = w;
8234                     y = 0;
8235                 break;
8236             }
8237             if(local === true){
8238                 return [x, y];
8239             }
8240             if(vp){
8241                 var sc = this.getScroll();
8242                 return [x + sc.left, y + sc.top];
8243             }
8244             //Add the element's offset xy
8245             var o = this.getXY();
8246             return [x+o[0], y+o[1]];
8247         },
8248
8249         /**
8250          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8251          * supported position values.
8252          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8253          * @param {String} position The position to align to.
8254          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8255          * @return {Array} [x, y]
8256          */
8257         getAlignToXY : function(el, p, o){
8258             el = Roo.get(el);
8259             var d = this.dom;
8260             if(!el.dom){
8261                 throw "Element.alignTo with an element that doesn't exist";
8262             }
8263             var c = false; //constrain to viewport
8264             var p1 = "", p2 = "";
8265             o = o || [0,0];
8266
8267             if(!p){
8268                 p = "tl-bl";
8269             }else if(p == "?"){
8270                 p = "tl-bl?";
8271             }else if(p.indexOf("-") == -1){
8272                 p = "tl-" + p;
8273             }
8274             p = p.toLowerCase();
8275             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8276             if(!m){
8277                throw "Element.alignTo with an invalid alignment " + p;
8278             }
8279             p1 = m[1]; p2 = m[2]; c = !!m[3];
8280
8281             //Subtract the aligned el's internal xy from the target's offset xy
8282             //plus custom offset to get the aligned el's new offset xy
8283             var a1 = this.getAnchorXY(p1, true);
8284             var a2 = el.getAnchorXY(p2, false);
8285             var x = a2[0] - a1[0] + o[0];
8286             var y = a2[1] - a1[1] + o[1];
8287             if(c){
8288                 //constrain the aligned el to viewport if necessary
8289                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8290                 // 5px of margin for ie
8291                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8292
8293                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8294                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8295                 //otherwise swap the aligned el to the opposite border of the target.
8296                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8297                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8298                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8299                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8300
8301                var doc = document;
8302                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8303                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8304
8305                if((x+w) > dw + scrollX){
8306                     x = swapX ? r.left-w : dw+scrollX-w;
8307                 }
8308                if(x < scrollX){
8309                    x = swapX ? r.right : scrollX;
8310                }
8311                if((y+h) > dh + scrollY){
8312                     y = swapY ? r.top-h : dh+scrollY-h;
8313                 }
8314                if (y < scrollY){
8315                    y = swapY ? r.bottom : scrollY;
8316                }
8317             }
8318             return [x,y];
8319         },
8320
8321         // private
8322         getConstrainToXY : function(){
8323             var os = {top:0, left:0, bottom:0, right: 0};
8324
8325             return function(el, local, offsets, proposedXY){
8326                 el = Roo.get(el);
8327                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8328
8329                 var vw, vh, vx = 0, vy = 0;
8330                 if(el.dom == document.body || el.dom == document){
8331                     vw = Roo.lib.Dom.getViewWidth();
8332                     vh = Roo.lib.Dom.getViewHeight();
8333                 }else{
8334                     vw = el.dom.clientWidth;
8335                     vh = el.dom.clientHeight;
8336                     if(!local){
8337                         var vxy = el.getXY();
8338                         vx = vxy[0];
8339                         vy = vxy[1];
8340                     }
8341                 }
8342
8343                 var s = el.getScroll();
8344
8345                 vx += offsets.left + s.left;
8346                 vy += offsets.top + s.top;
8347
8348                 vw -= offsets.right;
8349                 vh -= offsets.bottom;
8350
8351                 var vr = vx+vw;
8352                 var vb = vy+vh;
8353
8354                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8355                 var x = xy[0], y = xy[1];
8356                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8357
8358                 // only move it if it needs it
8359                 var moved = false;
8360
8361                 // first validate right/bottom
8362                 if((x + w) > vr){
8363                     x = vr - w;
8364                     moved = true;
8365                 }
8366                 if((y + h) > vb){
8367                     y = vb - h;
8368                     moved = true;
8369                 }
8370                 // then make sure top/left isn't negative
8371                 if(x < vx){
8372                     x = vx;
8373                     moved = true;
8374                 }
8375                 if(y < vy){
8376                     y = vy;
8377                     moved = true;
8378                 }
8379                 return moved ? [x, y] : false;
8380             };
8381         }(),
8382
8383         // private
8384         adjustForConstraints : function(xy, parent, offsets){
8385             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8386         },
8387
8388         /**
8389          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8390          * document it aligns it to the viewport.
8391          * The position parameter is optional, and can be specified in any one of the following formats:
8392          * <ul>
8393          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8394          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8395          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8396          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8397          *   <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
8398          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8399          * </ul>
8400          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8401          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8402          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8403          * that specified in order to enforce the viewport constraints.
8404          * Following are all of the supported anchor positions:
8405     <pre>
8406     Value  Description
8407     -----  -----------------------------
8408     tl     The top left corner (default)
8409     t      The center of the top edge
8410     tr     The top right corner
8411     l      The center of the left edge
8412     c      In the center of the element
8413     r      The center of the right edge
8414     bl     The bottom left corner
8415     b      The center of the bottom edge
8416     br     The bottom right corner
8417     </pre>
8418     Example Usage:
8419     <pre><code>
8420     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8421     el.alignTo("other-el");
8422
8423     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8424     el.alignTo("other-el", "tr?");
8425
8426     // align the bottom right corner of el with the center left edge of other-el
8427     el.alignTo("other-el", "br-l?");
8428
8429     // align the center of el with the bottom left corner of other-el and
8430     // adjust the x position by -6 pixels (and the y position by 0)
8431     el.alignTo("other-el", "c-bl", [-6, 0]);
8432     </code></pre>
8433          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8434          * @param {String} position The position to align to.
8435          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8436          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8437          * @return {Roo.Element} this
8438          */
8439         alignTo : function(element, position, offsets, animate){
8440             var xy = this.getAlignToXY(element, position, offsets);
8441             this.setXY(xy, this.preanim(arguments, 3));
8442             return this;
8443         },
8444
8445         /**
8446          * Anchors an element to another element and realigns it when the window is resized.
8447          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8448          * @param {String} position The position to align to.
8449          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8450          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8451          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8452          * is a number, it is used as the buffer delay (defaults to 50ms).
8453          * @param {Function} callback The function to call after the animation finishes
8454          * @return {Roo.Element} this
8455          */
8456         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8457             var action = function(){
8458                 this.alignTo(el, alignment, offsets, animate);
8459                 Roo.callback(callback, this);
8460             };
8461             Roo.EventManager.onWindowResize(action, this);
8462             var tm = typeof monitorScroll;
8463             if(tm != 'undefined'){
8464                 Roo.EventManager.on(window, 'scroll', action, this,
8465                     {buffer: tm == 'number' ? monitorScroll : 50});
8466             }
8467             action.call(this); // align immediately
8468             return this;
8469         },
8470         /**
8471          * Clears any opacity settings from this element. Required in some cases for IE.
8472          * @return {Roo.Element} this
8473          */
8474         clearOpacity : function(){
8475             if (window.ActiveXObject) {
8476                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8477                     this.dom.style.filter = "";
8478                 }
8479             } else {
8480                 this.dom.style.opacity = "";
8481                 this.dom.style["-moz-opacity"] = "";
8482                 this.dom.style["-khtml-opacity"] = "";
8483             }
8484             return this;
8485         },
8486
8487         /**
8488          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8489          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8490          * @return {Roo.Element} this
8491          */
8492         hide : function(animate){
8493             this.setVisible(false, this.preanim(arguments, 0));
8494             return this;
8495         },
8496
8497         /**
8498         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8499         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8500          * @return {Roo.Element} this
8501          */
8502         show : function(animate){
8503             this.setVisible(true, this.preanim(arguments, 0));
8504             return this;
8505         },
8506
8507         /**
8508          * @private Test if size has a unit, otherwise appends the default
8509          */
8510         addUnits : function(size){
8511             return Roo.Element.addUnits(size, this.defaultUnit);
8512         },
8513
8514         /**
8515          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8516          * @return {Roo.Element} this
8517          */
8518         beginMeasure : function(){
8519             var el = this.dom;
8520             if(el.offsetWidth || el.offsetHeight){
8521                 return this; // offsets work already
8522             }
8523             var changed = [];
8524             var p = this.dom, b = document.body; // start with this element
8525             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8526                 var pe = Roo.get(p);
8527                 if(pe.getStyle('display') == 'none'){
8528                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8529                     p.style.visibility = "hidden";
8530                     p.style.display = "block";
8531                 }
8532                 p = p.parentNode;
8533             }
8534             this._measureChanged = changed;
8535             return this;
8536
8537         },
8538
8539         /**
8540          * Restores displays to before beginMeasure was called
8541          * @return {Roo.Element} this
8542          */
8543         endMeasure : function(){
8544             var changed = this._measureChanged;
8545             if(changed){
8546                 for(var i = 0, len = changed.length; i < len; i++) {
8547                     var r = changed[i];
8548                     r.el.style.visibility = r.visibility;
8549                     r.el.style.display = "none";
8550                 }
8551                 this._measureChanged = null;
8552             }
8553             return this;
8554         },
8555
8556         /**
8557         * Update the innerHTML of this element, optionally searching for and processing scripts
8558         * @param {String} html The new HTML
8559         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8560         * @param {Function} callback For async script loading you can be noticed when the update completes
8561         * @return {Roo.Element} this
8562          */
8563         update : function(html, loadScripts, callback){
8564             if(typeof html == "undefined"){
8565                 html = "";
8566             }
8567             if(loadScripts !== true){
8568                 this.dom.innerHTML = html;
8569                 if(typeof callback == "function"){
8570                     callback();
8571                 }
8572                 return this;
8573             }
8574             var id = Roo.id();
8575             var dom = this.dom;
8576
8577             html += '<span id="' + id + '"></span>';
8578
8579             E.onAvailable(id, function(){
8580                 var hd = document.getElementsByTagName("head")[0];
8581                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8582                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8583                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8584
8585                 var match;
8586                 while(match = re.exec(html)){
8587                     var attrs = match[1];
8588                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8589                     if(srcMatch && srcMatch[2]){
8590                        var s = document.createElement("script");
8591                        s.src = srcMatch[2];
8592                        var typeMatch = attrs.match(typeRe);
8593                        if(typeMatch && typeMatch[2]){
8594                            s.type = typeMatch[2];
8595                        }
8596                        hd.appendChild(s);
8597                     }else if(match[2] && match[2].length > 0){
8598                         if(window.execScript) {
8599                            window.execScript(match[2]);
8600                         } else {
8601                             /**
8602                              * eval:var:id
8603                              * eval:var:dom
8604                              * eval:var:html
8605                              * 
8606                              */
8607                            window.eval(match[2]);
8608                         }
8609                     }
8610                 }
8611                 var el = document.getElementById(id);
8612                 if(el){el.parentNode.removeChild(el);}
8613                 if(typeof callback == "function"){
8614                     callback();
8615                 }
8616             });
8617             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8618             return this;
8619         },
8620
8621         /**
8622          * Direct access to the UpdateManager update() method (takes the same parameters).
8623          * @param {String/Function} url The url for this request or a function to call to get the url
8624          * @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}
8625          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8626          * @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.
8627          * @return {Roo.Element} this
8628          */
8629         load : function(){
8630             var um = this.getUpdateManager();
8631             um.update.apply(um, arguments);
8632             return this;
8633         },
8634
8635         /**
8636         * Gets this element's UpdateManager
8637         * @return {Roo.UpdateManager} The UpdateManager
8638         */
8639         getUpdateManager : function(){
8640             if(!this.updateManager){
8641                 this.updateManager = new Roo.UpdateManager(this);
8642             }
8643             return this.updateManager;
8644         },
8645
8646         /**
8647          * Disables text selection for this element (normalized across browsers)
8648          * @return {Roo.Element} this
8649          */
8650         unselectable : function(){
8651             this.dom.unselectable = "on";
8652             this.swallowEvent("selectstart", true);
8653             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8654             this.addClass("x-unselectable");
8655             return this;
8656         },
8657
8658         /**
8659         * Calculates the x, y to center this element on the screen
8660         * @return {Array} The x, y values [x, y]
8661         */
8662         getCenterXY : function(){
8663             return this.getAlignToXY(document, 'c-c');
8664         },
8665
8666         /**
8667         * Centers the Element in either the viewport, or another Element.
8668         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8669         */
8670         center : function(centerIn){
8671             this.alignTo(centerIn || document, 'c-c');
8672             return this;
8673         },
8674
8675         /**
8676          * Tests various css rules/browsers to determine if this element uses a border box
8677          * @return {Boolean}
8678          */
8679         isBorderBox : function(){
8680             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8681         },
8682
8683         /**
8684          * Return a box {x, y, width, height} that can be used to set another elements
8685          * size/location to match this element.
8686          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8687          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8688          * @return {Object} box An object in the format {x, y, width, height}
8689          */
8690         getBox : function(contentBox, local){
8691             var xy;
8692             if(!local){
8693                 xy = this.getXY();
8694             }else{
8695                 var left = parseInt(this.getStyle("left"), 10) || 0;
8696                 var top = parseInt(this.getStyle("top"), 10) || 0;
8697                 xy = [left, top];
8698             }
8699             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8700             if(!contentBox){
8701                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8702             }else{
8703                 var l = this.getBorderWidth("l")+this.getPadding("l");
8704                 var r = this.getBorderWidth("r")+this.getPadding("r");
8705                 var t = this.getBorderWidth("t")+this.getPadding("t");
8706                 var b = this.getBorderWidth("b")+this.getPadding("b");
8707                 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)};
8708             }
8709             bx.right = bx.x + bx.width;
8710             bx.bottom = bx.y + bx.height;
8711             return bx;
8712         },
8713
8714         /**
8715          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8716          for more information about the sides.
8717          * @param {String} sides
8718          * @return {Number}
8719          */
8720         getFrameWidth : function(sides, onlyContentBox){
8721             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8722         },
8723
8724         /**
8725          * 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.
8726          * @param {Object} box The box to fill {x, y, width, height}
8727          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8728          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8729          * @return {Roo.Element} this
8730          */
8731         setBox : function(box, adjust, animate){
8732             var w = box.width, h = box.height;
8733             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8734                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8735                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8736             }
8737             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8738             return this;
8739         },
8740
8741         /**
8742          * Forces the browser to repaint this element
8743          * @return {Roo.Element} this
8744          */
8745          repaint : function(){
8746             var dom = this.dom;
8747             this.addClass("x-repaint");
8748             setTimeout(function(){
8749                 Roo.get(dom).removeClass("x-repaint");
8750             }, 1);
8751             return this;
8752         },
8753
8754         /**
8755          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8756          * then it returns the calculated width of the sides (see getPadding)
8757          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8758          * @return {Object/Number}
8759          */
8760         getMargins : function(side){
8761             if(!side){
8762                 return {
8763                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8764                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8765                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8766                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8767                 };
8768             }else{
8769                 return this.addStyles(side, El.margins);
8770              }
8771         },
8772
8773         // private
8774         addStyles : function(sides, styles){
8775             var val = 0, v, w;
8776             for(var i = 0, len = sides.length; i < len; i++){
8777                 v = this.getStyle(styles[sides.charAt(i)]);
8778                 if(v){
8779                      w = parseInt(v, 10);
8780                      if(w){ val += w; }
8781                 }
8782             }
8783             return val;
8784         },
8785
8786         /**
8787          * Creates a proxy element of this element
8788          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8789          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8790          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8791          * @return {Roo.Element} The new proxy element
8792          */
8793         createProxy : function(config, renderTo, matchBox){
8794             if(renderTo){
8795                 renderTo = Roo.getDom(renderTo);
8796             }else{
8797                 renderTo = document.body;
8798             }
8799             config = typeof config == "object" ?
8800                 config : {tag : "div", cls: config};
8801             var proxy = Roo.DomHelper.append(renderTo, config, true);
8802             if(matchBox){
8803                proxy.setBox(this.getBox());
8804             }
8805             return proxy;
8806         },
8807
8808         /**
8809          * Puts a mask over this element to disable user interaction. Requires core.css.
8810          * This method can only be applied to elements which accept child nodes.
8811          * @param {String} msg (optional) A message to display in the mask
8812          * @param {String} msgCls (optional) A css class to apply to the msg element
8813          * @return {Element} The mask  element
8814          */
8815         mask : function(msg, msgCls){
8816             if(this.getStyle("position") == "static"){
8817                 this.setStyle("position", "relative");
8818             }
8819             if(!this._mask){
8820                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8821             }
8822             this.addClass("x-masked");
8823             this._mask.setDisplayed(true);
8824             if(typeof msg == 'string'){
8825                 if(!this._maskMsg){
8826                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8827                 }
8828                 var mm = this._maskMsg;
8829                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8830                 mm.dom.firstChild.innerHTML = msg;
8831                 mm.setDisplayed(true);
8832                 mm.center(this);
8833             }
8834             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8835                 this._mask.setHeight(this.getHeight());
8836             }
8837             return this._mask;
8838         },
8839
8840         /**
8841          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8842          * it is cached for reuse.
8843          */
8844         unmask : function(removeEl){
8845             if(this._mask){
8846                 if(removeEl === true){
8847                     this._mask.remove();
8848                     delete this._mask;
8849                     if(this._maskMsg){
8850                         this._maskMsg.remove();
8851                         delete this._maskMsg;
8852                     }
8853                 }else{
8854                     this._mask.setDisplayed(false);
8855                     if(this._maskMsg){
8856                         this._maskMsg.setDisplayed(false);
8857                     }
8858                 }
8859             }
8860             this.removeClass("x-masked");
8861         },
8862
8863         /**
8864          * Returns true if this element is masked
8865          * @return {Boolean}
8866          */
8867         isMasked : function(){
8868             return this._mask && this._mask.isVisible();
8869         },
8870
8871         /**
8872          * Creates an iframe shim for this element to keep selects and other windowed objects from
8873          * showing through.
8874          * @return {Roo.Element} The new shim element
8875          */
8876         createShim : function(){
8877             var el = document.createElement('iframe');
8878             el.frameBorder = 'no';
8879             el.className = 'roo-shim';
8880             if(Roo.isIE && Roo.isSecure){
8881                 el.src = Roo.SSL_SECURE_URL;
8882             }
8883             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8884             shim.autoBoxAdjust = false;
8885             return shim;
8886         },
8887
8888         /**
8889          * Removes this element from the DOM and deletes it from the cache
8890          */
8891         remove : function(){
8892             if(this.dom.parentNode){
8893                 this.dom.parentNode.removeChild(this.dom);
8894             }
8895             delete El.cache[this.dom.id];
8896         },
8897
8898         /**
8899          * Sets up event handlers to add and remove a css class when the mouse is over this element
8900          * @param {String} className
8901          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8902          * mouseout events for children elements
8903          * @return {Roo.Element} this
8904          */
8905         addClassOnOver : function(className, preventFlicker){
8906             this.on("mouseover", function(){
8907                 Roo.fly(this, '_internal').addClass(className);
8908             }, this.dom);
8909             var removeFn = function(e){
8910                 if(preventFlicker !== true || !e.within(this, true)){
8911                     Roo.fly(this, '_internal').removeClass(className);
8912                 }
8913             };
8914             this.on("mouseout", removeFn, this.dom);
8915             return this;
8916         },
8917
8918         /**
8919          * Sets up event handlers to add and remove a css class when this element has the focus
8920          * @param {String} className
8921          * @return {Roo.Element} this
8922          */
8923         addClassOnFocus : function(className){
8924             this.on("focus", function(){
8925                 Roo.fly(this, '_internal').addClass(className);
8926             }, this.dom);
8927             this.on("blur", function(){
8928                 Roo.fly(this, '_internal').removeClass(className);
8929             }, this.dom);
8930             return this;
8931         },
8932         /**
8933          * 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)
8934          * @param {String} className
8935          * @return {Roo.Element} this
8936          */
8937         addClassOnClick : function(className){
8938             var dom = this.dom;
8939             this.on("mousedown", function(){
8940                 Roo.fly(dom, '_internal').addClass(className);
8941                 var d = Roo.get(document);
8942                 var fn = function(){
8943                     Roo.fly(dom, '_internal').removeClass(className);
8944                     d.removeListener("mouseup", fn);
8945                 };
8946                 d.on("mouseup", fn);
8947             });
8948             return this;
8949         },
8950
8951         /**
8952          * Stops the specified event from bubbling and optionally prevents the default action
8953          * @param {String} eventName
8954          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8955          * @return {Roo.Element} this
8956          */
8957         swallowEvent : function(eventName, preventDefault){
8958             var fn = function(e){
8959                 e.stopPropagation();
8960                 if(preventDefault){
8961                     e.preventDefault();
8962                 }
8963             };
8964             if(eventName instanceof Array){
8965                 for(var i = 0, len = eventName.length; i < len; i++){
8966                      this.on(eventName[i], fn);
8967                 }
8968                 return this;
8969             }
8970             this.on(eventName, fn);
8971             return this;
8972         },
8973
8974         /**
8975          * @private
8976          */
8977       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8978
8979         /**
8980          * Sizes this element to its parent element's dimensions performing
8981          * neccessary box adjustments.
8982          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8983          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8984          * @return {Roo.Element} this
8985          */
8986         fitToParent : function(monitorResize, targetParent) {
8987           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
8988           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
8989           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
8990             return;
8991           }
8992           var p = Roo.get(targetParent || this.dom.parentNode);
8993           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
8994           if (monitorResize === true) {
8995             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
8996             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
8997           }
8998           return this;
8999         },
9000
9001         /**
9002          * Gets the next sibling, skipping text nodes
9003          * @return {HTMLElement} The next sibling or null
9004          */
9005         getNextSibling : function(){
9006             var n = this.dom.nextSibling;
9007             while(n && n.nodeType != 1){
9008                 n = n.nextSibling;
9009             }
9010             return n;
9011         },
9012
9013         /**
9014          * Gets the previous sibling, skipping text nodes
9015          * @return {HTMLElement} The previous sibling or null
9016          */
9017         getPrevSibling : function(){
9018             var n = this.dom.previousSibling;
9019             while(n && n.nodeType != 1){
9020                 n = n.previousSibling;
9021             }
9022             return n;
9023         },
9024
9025
9026         /**
9027          * Appends the passed element(s) to this element
9028          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9029          * @return {Roo.Element} this
9030          */
9031         appendChild: function(el){
9032             el = Roo.get(el);
9033             el.appendTo(this);
9034             return this;
9035         },
9036
9037         /**
9038          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9039          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9040          * automatically generated with the specified attributes.
9041          * @param {HTMLElement} insertBefore (optional) a child element of this element
9042          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9043          * @return {Roo.Element} The new child element
9044          */
9045         createChild: function(config, insertBefore, returnDom){
9046             config = config || {tag:'div'};
9047             if(insertBefore){
9048                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9049             }
9050             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9051         },
9052
9053         /**
9054          * Appends this element to the passed element
9055          * @param {String/HTMLElement/Element} el The new parent element
9056          * @return {Roo.Element} this
9057          */
9058         appendTo: function(el){
9059             el = Roo.getDom(el);
9060             el.appendChild(this.dom);
9061             return this;
9062         },
9063
9064         /**
9065          * Inserts this element before the passed element in the DOM
9066          * @param {String/HTMLElement/Element} el The element to insert before
9067          * @return {Roo.Element} this
9068          */
9069         insertBefore: function(el){
9070             el = Roo.getDom(el);
9071             el.parentNode.insertBefore(this.dom, el);
9072             return this;
9073         },
9074
9075         /**
9076          * Inserts this element after the passed element in the DOM
9077          * @param {String/HTMLElement/Element} el The element to insert after
9078          * @return {Roo.Element} this
9079          */
9080         insertAfter: function(el){
9081             el = Roo.getDom(el);
9082             el.parentNode.insertBefore(this.dom, el.nextSibling);
9083             return this;
9084         },
9085
9086         /**
9087          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9088          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9089          * @return {Roo.Element} The new child
9090          */
9091         insertFirst: function(el, returnDom){
9092             el = el || {};
9093             if(typeof el == 'object' && !el.nodeType){ // dh config
9094                 return this.createChild(el, this.dom.firstChild, returnDom);
9095             }else{
9096                 el = Roo.getDom(el);
9097                 this.dom.insertBefore(el, this.dom.firstChild);
9098                 return !returnDom ? Roo.get(el) : el;
9099             }
9100         },
9101
9102         /**
9103          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9104          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9105          * @param {String} where (optional) 'before' or 'after' defaults to before
9106          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9107          * @return {Roo.Element} the inserted Element
9108          */
9109         insertSibling: function(el, where, returnDom){
9110             where = where ? where.toLowerCase() : 'before';
9111             el = el || {};
9112             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9113
9114             if(typeof el == 'object' && !el.nodeType){ // dh config
9115                 if(where == 'after' && !this.dom.nextSibling){
9116                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9117                 }else{
9118                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9119                 }
9120
9121             }else{
9122                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9123                             where == 'before' ? this.dom : this.dom.nextSibling);
9124                 if(!returnDom){
9125                     rt = Roo.get(rt);
9126                 }
9127             }
9128             return rt;
9129         },
9130
9131         /**
9132          * Creates and wraps this element with another element
9133          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9134          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9135          * @return {HTMLElement/Element} The newly created wrapper element
9136          */
9137         wrap: function(config, returnDom){
9138             if(!config){
9139                 config = {tag: "div"};
9140             }
9141             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9142             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9143             return newEl;
9144         },
9145
9146         /**
9147          * Replaces the passed element with this element
9148          * @param {String/HTMLElement/Element} el The element to replace
9149          * @return {Roo.Element} this
9150          */
9151         replace: function(el){
9152             el = Roo.get(el);
9153             this.insertBefore(el);
9154             el.remove();
9155             return this;
9156         },
9157
9158         /**
9159          * Inserts an html fragment into this element
9160          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9161          * @param {String} html The HTML fragment
9162          * @param {Boolean} returnEl True to return an Roo.Element
9163          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9164          */
9165         insertHtml : function(where, html, returnEl){
9166             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9167             return returnEl ? Roo.get(el) : el;
9168         },
9169
9170         /**
9171          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9172          * @param {Object} o The object with the attributes
9173          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9174          * @return {Roo.Element} this
9175          */
9176         set : function(o, useSet){
9177             var el = this.dom;
9178             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9179             for(var attr in o){
9180                 if(attr == "style" || typeof o[attr] == "function") continue;
9181                 if(attr=="cls"){
9182                     el.className = o["cls"];
9183                 }else{
9184                     if(useSet) el.setAttribute(attr, o[attr]);
9185                     else el[attr] = o[attr];
9186                 }
9187             }
9188             if(o.style){
9189                 Roo.DomHelper.applyStyles(el, o.style);
9190             }
9191             return this;
9192         },
9193
9194         /**
9195          * Convenience method for constructing a KeyMap
9196          * @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:
9197          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9198          * @param {Function} fn The function to call
9199          * @param {Object} scope (optional) The scope of the function
9200          * @return {Roo.KeyMap} The KeyMap created
9201          */
9202         addKeyListener : function(key, fn, scope){
9203             var config;
9204             if(typeof key != "object" || key instanceof Array){
9205                 config = {
9206                     key: key,
9207                     fn: fn,
9208                     scope: scope
9209                 };
9210             }else{
9211                 config = {
9212                     key : key.key,
9213                     shift : key.shift,
9214                     ctrl : key.ctrl,
9215                     alt : key.alt,
9216                     fn: fn,
9217                     scope: scope
9218                 };
9219             }
9220             return new Roo.KeyMap(this, config);
9221         },
9222
9223         /**
9224          * Creates a KeyMap for this element
9225          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9226          * @return {Roo.KeyMap} The KeyMap created
9227          */
9228         addKeyMap : function(config){
9229             return new Roo.KeyMap(this, config);
9230         },
9231
9232         /**
9233          * Returns true if this element is scrollable.
9234          * @return {Boolean}
9235          */
9236          isScrollable : function(){
9237             var dom = this.dom;
9238             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9239         },
9240
9241         /**
9242          * 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().
9243          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9244          * @param {Number} value The new scroll value
9245          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9246          * @return {Element} this
9247          */
9248
9249         scrollTo : function(side, value, animate){
9250             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9251             if(!animate || !A){
9252                 this.dom[prop] = value;
9253             }else{
9254                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9255                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9256             }
9257             return this;
9258         },
9259
9260         /**
9261          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9262          * within this element's scrollable range.
9263          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9264          * @param {Number} distance How far to scroll the element in pixels
9265          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9266          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9267          * was scrolled as far as it could go.
9268          */
9269          scroll : function(direction, distance, animate){
9270              if(!this.isScrollable()){
9271                  return;
9272              }
9273              var el = this.dom;
9274              var l = el.scrollLeft, t = el.scrollTop;
9275              var w = el.scrollWidth, h = el.scrollHeight;
9276              var cw = el.clientWidth, ch = el.clientHeight;
9277              direction = direction.toLowerCase();
9278              var scrolled = false;
9279              var a = this.preanim(arguments, 2);
9280              switch(direction){
9281                  case "l":
9282                  case "left":
9283                      if(w - l > cw){
9284                          var v = Math.min(l + distance, w-cw);
9285                          this.scrollTo("left", v, a);
9286                          scrolled = true;
9287                      }
9288                      break;
9289                 case "r":
9290                 case "right":
9291                      if(l > 0){
9292                          var v = Math.max(l - distance, 0);
9293                          this.scrollTo("left", v, a);
9294                          scrolled = true;
9295                      }
9296                      break;
9297                 case "t":
9298                 case "top":
9299                 case "up":
9300                      if(t > 0){
9301                          var v = Math.max(t - distance, 0);
9302                          this.scrollTo("top", v, a);
9303                          scrolled = true;
9304                      }
9305                      break;
9306                 case "b":
9307                 case "bottom":
9308                 case "down":
9309                      if(h - t > ch){
9310                          var v = Math.min(t + distance, h-ch);
9311                          this.scrollTo("top", v, a);
9312                          scrolled = true;
9313                      }
9314                      break;
9315              }
9316              return scrolled;
9317         },
9318
9319         /**
9320          * Translates the passed page coordinates into left/top css values for this element
9321          * @param {Number/Array} x The page x or an array containing [x, y]
9322          * @param {Number} y The page y
9323          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9324          */
9325         translatePoints : function(x, y){
9326             if(typeof x == 'object' || x instanceof Array){
9327                 y = x[1]; x = x[0];
9328             }
9329             var p = this.getStyle('position');
9330             var o = this.getXY();
9331
9332             var l = parseInt(this.getStyle('left'), 10);
9333             var t = parseInt(this.getStyle('top'), 10);
9334
9335             if(isNaN(l)){
9336                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9337             }
9338             if(isNaN(t)){
9339                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9340             }
9341
9342             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9343         },
9344
9345         /**
9346          * Returns the current scroll position of the element.
9347          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9348          */
9349         getScroll : function(){
9350             var d = this.dom, doc = document;
9351             if(d == doc || d == doc.body){
9352                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9353                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9354                 return {left: l, top: t};
9355             }else{
9356                 return {left: d.scrollLeft, top: d.scrollTop};
9357             }
9358         },
9359
9360         /**
9361          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9362          * are convert to standard 6 digit hex color.
9363          * @param {String} attr The css attribute
9364          * @param {String} defaultValue The default value to use when a valid color isn't found
9365          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9366          * YUI color anims.
9367          */
9368         getColor : function(attr, defaultValue, prefix){
9369             var v = this.getStyle(attr);
9370             if(!v || v == "transparent" || v == "inherit") {
9371                 return defaultValue;
9372             }
9373             var color = typeof prefix == "undefined" ? "#" : prefix;
9374             if(v.substr(0, 4) == "rgb("){
9375                 var rvs = v.slice(4, v.length -1).split(",");
9376                 for(var i = 0; i < 3; i++){
9377                     var h = parseInt(rvs[i]).toString(16);
9378                     if(h < 16){
9379                         h = "0" + h;
9380                     }
9381                     color += h;
9382                 }
9383             } else {
9384                 if(v.substr(0, 1) == "#"){
9385                     if(v.length == 4) {
9386                         for(var i = 1; i < 4; i++){
9387                             var c = v.charAt(i);
9388                             color +=  c + c;
9389                         }
9390                     }else if(v.length == 7){
9391                         color += v.substr(1);
9392                     }
9393                 }
9394             }
9395             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9396         },
9397
9398         /**
9399          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9400          * gradient background, rounded corners and a 4-way shadow.
9401          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9402          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9403          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9404          * @return {Roo.Element} this
9405          */
9406         boxWrap : function(cls){
9407             cls = cls || 'x-box';
9408             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9409             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9410             return el;
9411         },
9412
9413         /**
9414          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9415          * @param {String} namespace The namespace in which to look for the attribute
9416          * @param {String} name The attribute name
9417          * @return {String} The attribute value
9418          */
9419         getAttributeNS : Roo.isIE ? function(ns, name){
9420             var d = this.dom;
9421             var type = typeof d[ns+":"+name];
9422             if(type != 'undefined' && type != 'unknown'){
9423                 return d[ns+":"+name];
9424             }
9425             return d[name];
9426         } : function(ns, name){
9427             var d = this.dom;
9428             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9429         }
9430     };
9431
9432     var ep = El.prototype;
9433
9434     /**
9435      * Appends an event handler (Shorthand for addListener)
9436      * @param {String}   eventName     The type of event to append
9437      * @param {Function} fn        The method the event invokes
9438      * @param {Object} scope       (optional) The scope (this object) of the fn
9439      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9440      * @method
9441      */
9442     ep.on = ep.addListener;
9443         // backwards compat
9444     ep.mon = ep.addListener;
9445
9446     /**
9447      * Removes an event handler from this element (shorthand for removeListener)
9448      * @param {String} eventName the type of event to remove
9449      * @param {Function} fn the method the event invokes
9450      * @return {Roo.Element} this
9451      * @method
9452      */
9453     ep.un = ep.removeListener;
9454
9455     /**
9456      * true to automatically adjust width and height settings for box-model issues (default to true)
9457      */
9458     ep.autoBoxAdjust = true;
9459
9460     // private
9461     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9462
9463     // private
9464     El.addUnits = function(v, defaultUnit){
9465         if(v === "" || v == "auto"){
9466             return v;
9467         }
9468         if(v === undefined){
9469             return '';
9470         }
9471         if(typeof v == "number" || !El.unitPattern.test(v)){
9472             return v + (defaultUnit || 'px');
9473         }
9474         return v;
9475     };
9476
9477     // special markup used throughout Roo when box wrapping elements
9478     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>';
9479     /**
9480      * Visibility mode constant - Use visibility to hide element
9481      * @static
9482      * @type Number
9483      */
9484     El.VISIBILITY = 1;
9485     /**
9486      * Visibility mode constant - Use display to hide element
9487      * @static
9488      * @type Number
9489      */
9490     El.DISPLAY = 2;
9491
9492     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9493     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9494     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9495
9496
9497
9498     /**
9499      * @private
9500      */
9501     El.cache = {};
9502
9503     var docEl;
9504
9505     /**
9506      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9507      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9508      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9509      * @return {Element} The Element object
9510      * @static
9511      */
9512     El.get = function(el){
9513         var ex, elm, id;
9514         if(!el){ return null; }
9515         if(typeof el == "string"){ // element id
9516             if(!(elm = document.getElementById(el))){
9517                 return null;
9518             }
9519             if(ex = El.cache[el]){
9520                 ex.dom = elm;
9521             }else{
9522                 ex = El.cache[el] = new El(elm);
9523             }
9524             return ex;
9525         }else if(el.tagName){ // dom element
9526             if(!(id = el.id)){
9527                 id = Roo.id(el);
9528             }
9529             if(ex = El.cache[id]){
9530                 ex.dom = el;
9531             }else{
9532                 ex = El.cache[id] = new El(el);
9533             }
9534             return ex;
9535         }else if(el instanceof El){
9536             if(el != docEl){
9537                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9538                                                               // catch case where it hasn't been appended
9539                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9540             }
9541             return el;
9542         }else if(el.isComposite){
9543             return el;
9544         }else if(el instanceof Array){
9545             return El.select(el);
9546         }else if(el == document){
9547             // create a bogus element object representing the document object
9548             if(!docEl){
9549                 var f = function(){};
9550                 f.prototype = El.prototype;
9551                 docEl = new f();
9552                 docEl.dom = document;
9553             }
9554             return docEl;
9555         }
9556         return null;
9557     };
9558
9559     // private
9560     El.uncache = function(el){
9561         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9562             if(a[i]){
9563                 delete El.cache[a[i].id || a[i]];
9564             }
9565         }
9566     };
9567
9568     // private
9569     // Garbage collection - uncache elements/purge listeners on orphaned elements
9570     // so we don't hold a reference and cause the browser to retain them
9571     El.garbageCollect = function(){
9572         if(!Roo.enableGarbageCollector){
9573             clearInterval(El.collectorThread);
9574             return;
9575         }
9576         for(var eid in El.cache){
9577             var el = El.cache[eid], d = el.dom;
9578             // -------------------------------------------------------
9579             // Determining what is garbage:
9580             // -------------------------------------------------------
9581             // !d
9582             // dom node is null, definitely garbage
9583             // -------------------------------------------------------
9584             // !d.parentNode
9585             // no parentNode == direct orphan, definitely garbage
9586             // -------------------------------------------------------
9587             // !d.offsetParent && !document.getElementById(eid)
9588             // display none elements have no offsetParent so we will
9589             // also try to look it up by it's id. However, check
9590             // offsetParent first so we don't do unneeded lookups.
9591             // This enables collection of elements that are not orphans
9592             // directly, but somewhere up the line they have an orphan
9593             // parent.
9594             // -------------------------------------------------------
9595             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9596                 delete El.cache[eid];
9597                 if(d && Roo.enableListenerCollection){
9598                     E.purgeElement(d);
9599                 }
9600             }
9601         }
9602     }
9603     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9604
9605
9606     // dom is optional
9607     El.Flyweight = function(dom){
9608         this.dom = dom;
9609     };
9610     El.Flyweight.prototype = El.prototype;
9611
9612     El._flyweights = {};
9613     /**
9614      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9615      * the dom node can be overwritten by other code.
9616      * @param {String/HTMLElement} el The dom node or id
9617      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9618      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9619      * @static
9620      * @return {Element} The shared Element object
9621      */
9622     El.fly = function(el, named){
9623         named = named || '_global';
9624         el = Roo.getDom(el);
9625         if(!el){
9626             return null;
9627         }
9628         if(!El._flyweights[named]){
9629             El._flyweights[named] = new El.Flyweight();
9630         }
9631         El._flyweights[named].dom = el;
9632         return El._flyweights[named];
9633     };
9634
9635     /**
9636      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9637      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9638      * Shorthand of {@link Roo.Element#get}
9639      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9640      * @return {Element} The Element object
9641      * @member Roo
9642      * @method get
9643      */
9644     Roo.get = El.get;
9645     /**
9646      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9647      * the dom node can be overwritten by other code.
9648      * Shorthand of {@link Roo.Element#fly}
9649      * @param {String/HTMLElement} el The dom node or id
9650      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9651      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9652      * @static
9653      * @return {Element} The shared Element object
9654      * @member Roo
9655      * @method fly
9656      */
9657     Roo.fly = El.fly;
9658
9659     // speedy lookup for elements never to box adjust
9660     var noBoxAdjust = Roo.isStrict ? {
9661         select:1
9662     } : {
9663         input:1, select:1, textarea:1
9664     };
9665     if(Roo.isIE || Roo.isGecko){
9666         noBoxAdjust['button'] = 1;
9667     }
9668
9669
9670     Roo.EventManager.on(window, 'unload', function(){
9671         delete El.cache;
9672         delete El._flyweights;
9673     });
9674 })();
9675
9676
9677
9678
9679 if(Roo.DomQuery){
9680     Roo.Element.selectorFunction = Roo.DomQuery.select;
9681 }
9682
9683 Roo.Element.select = function(selector, unique, root){
9684     var els;
9685     if(typeof selector == "string"){
9686         els = Roo.Element.selectorFunction(selector, root);
9687     }else if(selector.length !== undefined){
9688         els = selector;
9689     }else{
9690         throw "Invalid selector";
9691     }
9692     if(unique === true){
9693         return new Roo.CompositeElement(els);
9694     }else{
9695         return new Roo.CompositeElementLite(els);
9696     }
9697 };
9698 /**
9699  * Selects elements based on the passed CSS selector to enable working on them as 1.
9700  * @param {String/Array} selector The CSS selector or an array of elements
9701  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9702  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9703  * @return {CompositeElementLite/CompositeElement}
9704  * @member Roo
9705  * @method select
9706  */
9707 Roo.select = Roo.Element.select;
9708
9709
9710
9711
9712
9713
9714
9715
9716
9717
9718
9719
9720
9721
9722 /*
9723  * Based on:
9724  * Ext JS Library 1.1.1
9725  * Copyright(c) 2006-2007, Ext JS, LLC.
9726  *
9727  * Originally Released Under LGPL - original licence link has changed is not relivant.
9728  *
9729  * Fork - LGPL
9730  * <script type="text/javascript">
9731  */
9732
9733
9734
9735 //Notifies Element that fx methods are available
9736 Roo.enableFx = true;
9737
9738 /**
9739  * @class Roo.Fx
9740  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9741  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9742  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9743  * Element effects to work.</p><br/>
9744  *
9745  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9746  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9747  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9748  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9749  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9750  * expected results and should be done with care.</p><br/>
9751  *
9752  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9753  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9754 <pre>
9755 Value  Description
9756 -----  -----------------------------
9757 tl     The top left corner
9758 t      The center of the top edge
9759 tr     The top right corner
9760 l      The center of the left edge
9761 r      The center of the right edge
9762 bl     The bottom left corner
9763 b      The center of the bottom edge
9764 br     The bottom right corner
9765 </pre>
9766  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9767  * below are common options that can be passed to any Fx method.</b>
9768  * @cfg {Function} callback A function called when the effect is finished
9769  * @cfg {Object} scope The scope of the effect function
9770  * @cfg {String} easing A valid Easing value for the effect
9771  * @cfg {String} afterCls A css class to apply after the effect
9772  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9773  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9774  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9775  * effects that end with the element being visually hidden, ignored otherwise)
9776  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9777  * a function which returns such a specification that will be applied to the Element after the effect finishes
9778  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9779  * @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
9780  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9781  */
9782 Roo.Fx = {
9783         /**
9784          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9785          * origin for the slide effect.  This function automatically handles wrapping the element with
9786          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9787          * Usage:
9788          *<pre><code>
9789 // default: slide the element in from the top
9790 el.slideIn();
9791
9792 // custom: slide the element in from the right with a 2-second duration
9793 el.slideIn('r', { duration: 2 });
9794
9795 // common config options shown with default values
9796 el.slideIn('t', {
9797     easing: 'easeOut',
9798     duration: .5
9799 });
9800 </code></pre>
9801          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9802          * @param {Object} options (optional) Object literal with any of the Fx config options
9803          * @return {Roo.Element} The Element
9804          */
9805     slideIn : function(anchor, o){
9806         var el = this.getFxEl();
9807         o = o || {};
9808
9809         el.queueFx(o, function(){
9810
9811             anchor = anchor || "t";
9812
9813             // fix display to visibility
9814             this.fixDisplay();
9815
9816             // restore values after effect
9817             var r = this.getFxRestore();
9818             var b = this.getBox();
9819             // fixed size for slide
9820             this.setSize(b);
9821
9822             // wrap if needed
9823             var wrap = this.fxWrap(r.pos, o, "hidden");
9824
9825             var st = this.dom.style;
9826             st.visibility = "visible";
9827             st.position = "absolute";
9828
9829             // clear out temp styles after slide and unwrap
9830             var after = function(){
9831                 el.fxUnwrap(wrap, r.pos, o);
9832                 st.width = r.width;
9833                 st.height = r.height;
9834                 el.afterFx(o);
9835             };
9836             // time to calc the positions
9837             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9838
9839             switch(anchor.toLowerCase()){
9840                 case "t":
9841                     wrap.setSize(b.width, 0);
9842                     st.left = st.bottom = "0";
9843                     a = {height: bh};
9844                 break;
9845                 case "l":
9846                     wrap.setSize(0, b.height);
9847                     st.right = st.top = "0";
9848                     a = {width: bw};
9849                 break;
9850                 case "r":
9851                     wrap.setSize(0, b.height);
9852                     wrap.setX(b.right);
9853                     st.left = st.top = "0";
9854                     a = {width: bw, points: pt};
9855                 break;
9856                 case "b":
9857                     wrap.setSize(b.width, 0);
9858                     wrap.setY(b.bottom);
9859                     st.left = st.top = "0";
9860                     a = {height: bh, points: pt};
9861                 break;
9862                 case "tl":
9863                     wrap.setSize(0, 0);
9864                     st.right = st.bottom = "0";
9865                     a = {width: bw, height: bh};
9866                 break;
9867                 case "bl":
9868                     wrap.setSize(0, 0);
9869                     wrap.setY(b.y+b.height);
9870                     st.right = st.top = "0";
9871                     a = {width: bw, height: bh, points: pt};
9872                 break;
9873                 case "br":
9874                     wrap.setSize(0, 0);
9875                     wrap.setXY([b.right, b.bottom]);
9876                     st.left = st.top = "0";
9877                     a = {width: bw, height: bh, points: pt};
9878                 break;
9879                 case "tr":
9880                     wrap.setSize(0, 0);
9881                     wrap.setX(b.x+b.width);
9882                     st.left = st.bottom = "0";
9883                     a = {width: bw, height: bh, points: pt};
9884                 break;
9885             }
9886             this.dom.style.visibility = "visible";
9887             wrap.show();
9888
9889             arguments.callee.anim = wrap.fxanim(a,
9890                 o,
9891                 'motion',
9892                 .5,
9893                 'easeOut', after);
9894         });
9895         return this;
9896     },
9897     
9898         /**
9899          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9900          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9901          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9902          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9903          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9904          * Usage:
9905          *<pre><code>
9906 // default: slide the element out to the top
9907 el.slideOut();
9908
9909 // custom: slide the element out to the right with a 2-second duration
9910 el.slideOut('r', { duration: 2 });
9911
9912 // common config options shown with default values
9913 el.slideOut('t', {
9914     easing: 'easeOut',
9915     duration: .5,
9916     remove: false,
9917     useDisplay: false
9918 });
9919 </code></pre>
9920          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9921          * @param {Object} options (optional) Object literal with any of the Fx config options
9922          * @return {Roo.Element} The Element
9923          */
9924     slideOut : function(anchor, o){
9925         var el = this.getFxEl();
9926         o = o || {};
9927
9928         el.queueFx(o, function(){
9929
9930             anchor = anchor || "t";
9931
9932             // restore values after effect
9933             var r = this.getFxRestore();
9934             
9935             var b = this.getBox();
9936             // fixed size for slide
9937             this.setSize(b);
9938
9939             // wrap if needed
9940             var wrap = this.fxWrap(r.pos, o, "visible");
9941
9942             var st = this.dom.style;
9943             st.visibility = "visible";
9944             st.position = "absolute";
9945
9946             wrap.setSize(b);
9947
9948             var after = function(){
9949                 if(o.useDisplay){
9950                     el.setDisplayed(false);
9951                 }else{
9952                     el.hide();
9953                 }
9954
9955                 el.fxUnwrap(wrap, r.pos, o);
9956
9957                 st.width = r.width;
9958                 st.height = r.height;
9959
9960                 el.afterFx(o);
9961             };
9962
9963             var a, zero = {to: 0};
9964             switch(anchor.toLowerCase()){
9965                 case "t":
9966                     st.left = st.bottom = "0";
9967                     a = {height: zero};
9968                 break;
9969                 case "l":
9970                     st.right = st.top = "0";
9971                     a = {width: zero};
9972                 break;
9973                 case "r":
9974                     st.left = st.top = "0";
9975                     a = {width: zero, points: {to:[b.right, b.y]}};
9976                 break;
9977                 case "b":
9978                     st.left = st.top = "0";
9979                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9980                 break;
9981                 case "tl":
9982                     st.right = st.bottom = "0";
9983                     a = {width: zero, height: zero};
9984                 break;
9985                 case "bl":
9986                     st.right = st.top = "0";
9987                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
9988                 break;
9989                 case "br":
9990                     st.left = st.top = "0";
9991                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
9992                 break;
9993                 case "tr":
9994                     st.left = st.bottom = "0";
9995                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
9996                 break;
9997             }
9998
9999             arguments.callee.anim = wrap.fxanim(a,
10000                 o,
10001                 'motion',
10002                 .5,
10003                 "easeOut", after);
10004         });
10005         return this;
10006     },
10007
10008         /**
10009          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10010          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10011          * The element must be removed from the DOM using the 'remove' config option if desired.
10012          * Usage:
10013          *<pre><code>
10014 // default
10015 el.puff();
10016
10017 // common config options shown with default values
10018 el.puff({
10019     easing: 'easeOut',
10020     duration: .5,
10021     remove: false,
10022     useDisplay: false
10023 });
10024 </code></pre>
10025          * @param {Object} options (optional) Object literal with any of the Fx config options
10026          * @return {Roo.Element} The Element
10027          */
10028     puff : function(o){
10029         var el = this.getFxEl();
10030         o = o || {};
10031
10032         el.queueFx(o, function(){
10033             this.clearOpacity();
10034             this.show();
10035
10036             // restore values after effect
10037             var r = this.getFxRestore();
10038             var st = this.dom.style;
10039
10040             var after = function(){
10041                 if(o.useDisplay){
10042                     el.setDisplayed(false);
10043                 }else{
10044                     el.hide();
10045                 }
10046
10047                 el.clearOpacity();
10048
10049                 el.setPositioning(r.pos);
10050                 st.width = r.width;
10051                 st.height = r.height;
10052                 st.fontSize = '';
10053                 el.afterFx(o);
10054             };
10055
10056             var width = this.getWidth();
10057             var height = this.getHeight();
10058
10059             arguments.callee.anim = this.fxanim({
10060                     width : {to: this.adjustWidth(width * 2)},
10061                     height : {to: this.adjustHeight(height * 2)},
10062                     points : {by: [-(width * .5), -(height * .5)]},
10063                     opacity : {to: 0},
10064                     fontSize: {to:200, unit: "%"}
10065                 },
10066                 o,
10067                 'motion',
10068                 .5,
10069                 "easeOut", after);
10070         });
10071         return this;
10072     },
10073
10074         /**
10075          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10076          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10077          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10078          * Usage:
10079          *<pre><code>
10080 // default
10081 el.switchOff();
10082
10083 // all config options shown with default values
10084 el.switchOff({
10085     easing: 'easeIn',
10086     duration: .3,
10087     remove: false,
10088     useDisplay: false
10089 });
10090 </code></pre>
10091          * @param {Object} options (optional) Object literal with any of the Fx config options
10092          * @return {Roo.Element} The Element
10093          */
10094     switchOff : function(o){
10095         var el = this.getFxEl();
10096         o = o || {};
10097
10098         el.queueFx(o, function(){
10099             this.clearOpacity();
10100             this.clip();
10101
10102             // restore values after effect
10103             var r = this.getFxRestore();
10104             var st = this.dom.style;
10105
10106             var after = function(){
10107                 if(o.useDisplay){
10108                     el.setDisplayed(false);
10109                 }else{
10110                     el.hide();
10111                 }
10112
10113                 el.clearOpacity();
10114                 el.setPositioning(r.pos);
10115                 st.width = r.width;
10116                 st.height = r.height;
10117
10118                 el.afterFx(o);
10119             };
10120
10121             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10122                 this.clearOpacity();
10123                 (function(){
10124                     this.fxanim({
10125                         height:{to:1},
10126                         points:{by:[0, this.getHeight() * .5]}
10127                     }, o, 'motion', 0.3, 'easeIn', after);
10128                 }).defer(100, this);
10129             });
10130         });
10131         return this;
10132     },
10133
10134     /**
10135      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10136      * changed using the "attr" config option) and then fading back to the original color. If no original
10137      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10138      * Usage:
10139 <pre><code>
10140 // default: highlight background to yellow
10141 el.highlight();
10142
10143 // custom: highlight foreground text to blue for 2 seconds
10144 el.highlight("0000ff", { attr: 'color', duration: 2 });
10145
10146 // common config options shown with default values
10147 el.highlight("ffff9c", {
10148     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10149     endColor: (current color) or "ffffff",
10150     easing: 'easeIn',
10151     duration: 1
10152 });
10153 </code></pre>
10154      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10155      * @param {Object} options (optional) Object literal with any of the Fx config options
10156      * @return {Roo.Element} The Element
10157      */ 
10158     highlight : function(color, o){
10159         var el = this.getFxEl();
10160         o = o || {};
10161
10162         el.queueFx(o, function(){
10163             color = color || "ffff9c";
10164             attr = o.attr || "backgroundColor";
10165
10166             this.clearOpacity();
10167             this.show();
10168
10169             var origColor = this.getColor(attr);
10170             var restoreColor = this.dom.style[attr];
10171             endColor = (o.endColor || origColor) || "ffffff";
10172
10173             var after = function(){
10174                 el.dom.style[attr] = restoreColor;
10175                 el.afterFx(o);
10176             };
10177
10178             var a = {};
10179             a[attr] = {from: color, to: endColor};
10180             arguments.callee.anim = this.fxanim(a,
10181                 o,
10182                 'color',
10183                 1,
10184                 'easeIn', after);
10185         });
10186         return this;
10187     },
10188
10189    /**
10190     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10191     * Usage:
10192 <pre><code>
10193 // default: a single light blue ripple
10194 el.frame();
10195
10196 // custom: 3 red ripples lasting 3 seconds total
10197 el.frame("ff0000", 3, { duration: 3 });
10198
10199 // common config options shown with default values
10200 el.frame("C3DAF9", 1, {
10201     duration: 1 //duration of entire animation (not each individual ripple)
10202     // Note: Easing is not configurable and will be ignored if included
10203 });
10204 </code></pre>
10205     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10206     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10207     * @param {Object} options (optional) Object literal with any of the Fx config options
10208     * @return {Roo.Element} The Element
10209     */
10210     frame : function(color, count, o){
10211         var el = this.getFxEl();
10212         o = o || {};
10213
10214         el.queueFx(o, function(){
10215             color = color || "#C3DAF9";
10216             if(color.length == 6){
10217                 color = "#" + color;
10218             }
10219             count = count || 1;
10220             duration = o.duration || 1;
10221             this.show();
10222
10223             var b = this.getBox();
10224             var animFn = function(){
10225                 var proxy = this.createProxy({
10226
10227                      style:{
10228                         visbility:"hidden",
10229                         position:"absolute",
10230                         "z-index":"35000", // yee haw
10231                         border:"0px solid " + color
10232                      }
10233                   });
10234                 var scale = Roo.isBorderBox ? 2 : 1;
10235                 proxy.animate({
10236                     top:{from:b.y, to:b.y - 20},
10237                     left:{from:b.x, to:b.x - 20},
10238                     borderWidth:{from:0, to:10},
10239                     opacity:{from:1, to:0},
10240                     height:{from:b.height, to:(b.height + (20*scale))},
10241                     width:{from:b.width, to:(b.width + (20*scale))}
10242                 }, duration, function(){
10243                     proxy.remove();
10244                 });
10245                 if(--count > 0){
10246                      animFn.defer((duration/2)*1000, this);
10247                 }else{
10248                     el.afterFx(o);
10249                 }
10250             };
10251             animFn.call(this);
10252         });
10253         return this;
10254     },
10255
10256    /**
10257     * Creates a pause before any subsequent queued effects begin.  If there are
10258     * no effects queued after the pause it will have no effect.
10259     * Usage:
10260 <pre><code>
10261 el.pause(1);
10262 </code></pre>
10263     * @param {Number} seconds The length of time to pause (in seconds)
10264     * @return {Roo.Element} The Element
10265     */
10266     pause : function(seconds){
10267         var el = this.getFxEl();
10268         var o = {};
10269
10270         el.queueFx(o, function(){
10271             setTimeout(function(){
10272                 el.afterFx(o);
10273             }, seconds * 1000);
10274         });
10275         return this;
10276     },
10277
10278    /**
10279     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10280     * using the "endOpacity" config option.
10281     * Usage:
10282 <pre><code>
10283 // default: fade in from opacity 0 to 100%
10284 el.fadeIn();
10285
10286 // custom: fade in from opacity 0 to 75% over 2 seconds
10287 el.fadeIn({ endOpacity: .75, duration: 2});
10288
10289 // common config options shown with default values
10290 el.fadeIn({
10291     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10292     easing: 'easeOut',
10293     duration: .5
10294 });
10295 </code></pre>
10296     * @param {Object} options (optional) Object literal with any of the Fx config options
10297     * @return {Roo.Element} The Element
10298     */
10299     fadeIn : function(o){
10300         var el = this.getFxEl();
10301         o = o || {};
10302         el.queueFx(o, function(){
10303             this.setOpacity(0);
10304             this.fixDisplay();
10305             this.dom.style.visibility = 'visible';
10306             var to = o.endOpacity || 1;
10307             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10308                 o, null, .5, "easeOut", function(){
10309                 if(to == 1){
10310                     this.clearOpacity();
10311                 }
10312                 el.afterFx(o);
10313             });
10314         });
10315         return this;
10316     },
10317
10318    /**
10319     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10320     * using the "endOpacity" config option.
10321     * Usage:
10322 <pre><code>
10323 // default: fade out from the element's current opacity to 0
10324 el.fadeOut();
10325
10326 // custom: fade out from the element's current opacity to 25% over 2 seconds
10327 el.fadeOut({ endOpacity: .25, duration: 2});
10328
10329 // common config options shown with default values
10330 el.fadeOut({
10331     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10332     easing: 'easeOut',
10333     duration: .5
10334     remove: false,
10335     useDisplay: false
10336 });
10337 </code></pre>
10338     * @param {Object} options (optional) Object literal with any of the Fx config options
10339     * @return {Roo.Element} The Element
10340     */
10341     fadeOut : function(o){
10342         var el = this.getFxEl();
10343         o = o || {};
10344         el.queueFx(o, function(){
10345             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10346                 o, null, .5, "easeOut", function(){
10347                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10348                      this.dom.style.display = "none";
10349                 }else{
10350                      this.dom.style.visibility = "hidden";
10351                 }
10352                 this.clearOpacity();
10353                 el.afterFx(o);
10354             });
10355         });
10356         return this;
10357     },
10358
10359    /**
10360     * Animates the transition of an element's dimensions from a starting height/width
10361     * to an ending height/width.
10362     * Usage:
10363 <pre><code>
10364 // change height and width to 100x100 pixels
10365 el.scale(100, 100);
10366
10367 // common config options shown with default values.  The height and width will default to
10368 // the element's existing values if passed as null.
10369 el.scale(
10370     [element's width],
10371     [element's height], {
10372     easing: 'easeOut',
10373     duration: .35
10374 });
10375 </code></pre>
10376     * @param {Number} width  The new width (pass undefined to keep the original width)
10377     * @param {Number} height  The new height (pass undefined to keep the original height)
10378     * @param {Object} options (optional) Object literal with any of the Fx config options
10379     * @return {Roo.Element} The Element
10380     */
10381     scale : function(w, h, o){
10382         this.shift(Roo.apply({}, o, {
10383             width: w,
10384             height: h
10385         }));
10386         return this;
10387     },
10388
10389    /**
10390     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10391     * Any of these properties not specified in the config object will not be changed.  This effect 
10392     * requires that at least one new dimension, position or opacity setting must be passed in on
10393     * the config object in order for the function to have any effect.
10394     * Usage:
10395 <pre><code>
10396 // slide the element horizontally to x position 200 while changing the height and opacity
10397 el.shift({ x: 200, height: 50, opacity: .8 });
10398
10399 // common config options shown with default values.
10400 el.shift({
10401     width: [element's width],
10402     height: [element's height],
10403     x: [element's x position],
10404     y: [element's y position],
10405     opacity: [element's opacity],
10406     easing: 'easeOut',
10407     duration: .35
10408 });
10409 </code></pre>
10410     * @param {Object} options  Object literal with any of the Fx config options
10411     * @return {Roo.Element} The Element
10412     */
10413     shift : function(o){
10414         var el = this.getFxEl();
10415         o = o || {};
10416         el.queueFx(o, function(){
10417             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10418             if(w !== undefined){
10419                 a.width = {to: this.adjustWidth(w)};
10420             }
10421             if(h !== undefined){
10422                 a.height = {to: this.adjustHeight(h)};
10423             }
10424             if(x !== undefined || y !== undefined){
10425                 a.points = {to: [
10426                     x !== undefined ? x : this.getX(),
10427                     y !== undefined ? y : this.getY()
10428                 ]};
10429             }
10430             if(op !== undefined){
10431                 a.opacity = {to: op};
10432             }
10433             if(o.xy !== undefined){
10434                 a.points = {to: o.xy};
10435             }
10436             arguments.callee.anim = this.fxanim(a,
10437                 o, 'motion', .35, "easeOut", function(){
10438                 el.afterFx(o);
10439             });
10440         });
10441         return this;
10442     },
10443
10444         /**
10445          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10446          * ending point of the effect.
10447          * Usage:
10448          *<pre><code>
10449 // default: slide the element downward while fading out
10450 el.ghost();
10451
10452 // custom: slide the element out to the right with a 2-second duration
10453 el.ghost('r', { duration: 2 });
10454
10455 // common config options shown with default values
10456 el.ghost('b', {
10457     easing: 'easeOut',
10458     duration: .5
10459     remove: false,
10460     useDisplay: false
10461 });
10462 </code></pre>
10463          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10464          * @param {Object} options (optional) Object literal with any of the Fx config options
10465          * @return {Roo.Element} The Element
10466          */
10467     ghost : function(anchor, o){
10468         var el = this.getFxEl();
10469         o = o || {};
10470
10471         el.queueFx(o, function(){
10472             anchor = anchor || "b";
10473
10474             // restore values after effect
10475             var r = this.getFxRestore();
10476             var w = this.getWidth(),
10477                 h = this.getHeight();
10478
10479             var st = this.dom.style;
10480
10481             var after = function(){
10482                 if(o.useDisplay){
10483                     el.setDisplayed(false);
10484                 }else{
10485                     el.hide();
10486                 }
10487
10488                 el.clearOpacity();
10489                 el.setPositioning(r.pos);
10490                 st.width = r.width;
10491                 st.height = r.height;
10492
10493                 el.afterFx(o);
10494             };
10495
10496             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10497             switch(anchor.toLowerCase()){
10498                 case "t":
10499                     pt.by = [0, -h];
10500                 break;
10501                 case "l":
10502                     pt.by = [-w, 0];
10503                 break;
10504                 case "r":
10505                     pt.by = [w, 0];
10506                 break;
10507                 case "b":
10508                     pt.by = [0, h];
10509                 break;
10510                 case "tl":
10511                     pt.by = [-w, -h];
10512                 break;
10513                 case "bl":
10514                     pt.by = [-w, h];
10515                 break;
10516                 case "br":
10517                     pt.by = [w, h];
10518                 break;
10519                 case "tr":
10520                     pt.by = [w, -h];
10521                 break;
10522             }
10523
10524             arguments.callee.anim = this.fxanim(a,
10525                 o,
10526                 'motion',
10527                 .5,
10528                 "easeOut", after);
10529         });
10530         return this;
10531     },
10532
10533         /**
10534          * Ensures that all effects queued after syncFx is called on the element are
10535          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10536          * @return {Roo.Element} The Element
10537          */
10538     syncFx : function(){
10539         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10540             block : false,
10541             concurrent : true,
10542             stopFx : false
10543         });
10544         return this;
10545     },
10546
10547         /**
10548          * Ensures that all effects queued after sequenceFx is called on the element are
10549          * run in sequence.  This is the opposite of {@link #syncFx}.
10550          * @return {Roo.Element} The Element
10551          */
10552     sequenceFx : function(){
10553         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10554             block : false,
10555             concurrent : false,
10556             stopFx : false
10557         });
10558         return this;
10559     },
10560
10561         /* @private */
10562     nextFx : function(){
10563         var ef = this.fxQueue[0];
10564         if(ef){
10565             ef.call(this);
10566         }
10567     },
10568
10569         /**
10570          * Returns true if the element has any effects actively running or queued, else returns false.
10571          * @return {Boolean} True if element has active effects, else false
10572          */
10573     hasActiveFx : function(){
10574         return this.fxQueue && this.fxQueue[0];
10575     },
10576
10577         /**
10578          * Stops any running effects and clears the element's internal effects queue if it contains
10579          * any additional effects that haven't started yet.
10580          * @return {Roo.Element} The Element
10581          */
10582     stopFx : function(){
10583         if(this.hasActiveFx()){
10584             var cur = this.fxQueue[0];
10585             if(cur && cur.anim && cur.anim.isAnimated()){
10586                 this.fxQueue = [cur]; // clear out others
10587                 cur.anim.stop(true);
10588             }
10589         }
10590         return this;
10591     },
10592
10593         /* @private */
10594     beforeFx : function(o){
10595         if(this.hasActiveFx() && !o.concurrent){
10596            if(o.stopFx){
10597                this.stopFx();
10598                return true;
10599            }
10600            return false;
10601         }
10602         return true;
10603     },
10604
10605         /**
10606          * Returns true if the element is currently blocking so that no other effect can be queued
10607          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10608          * used to ensure that an effect initiated by a user action runs to completion prior to the
10609          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10610          * @return {Boolean} True if blocking, else false
10611          */
10612     hasFxBlock : function(){
10613         var q = this.fxQueue;
10614         return q && q[0] && q[0].block;
10615     },
10616
10617         /* @private */
10618     queueFx : function(o, fn){
10619         if(!this.fxQueue){
10620             this.fxQueue = [];
10621         }
10622         if(!this.hasFxBlock()){
10623             Roo.applyIf(o, this.fxDefaults);
10624             if(!o.concurrent){
10625                 var run = this.beforeFx(o);
10626                 fn.block = o.block;
10627                 this.fxQueue.push(fn);
10628                 if(run){
10629                     this.nextFx();
10630                 }
10631             }else{
10632                 fn.call(this);
10633             }
10634         }
10635         return this;
10636     },
10637
10638         /* @private */
10639     fxWrap : function(pos, o, vis){
10640         var wrap;
10641         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10642             var wrapXY;
10643             if(o.fixPosition){
10644                 wrapXY = this.getXY();
10645             }
10646             var div = document.createElement("div");
10647             div.style.visibility = vis;
10648             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10649             wrap.setPositioning(pos);
10650             if(wrap.getStyle("position") == "static"){
10651                 wrap.position("relative");
10652             }
10653             this.clearPositioning('auto');
10654             wrap.clip();
10655             wrap.dom.appendChild(this.dom);
10656             if(wrapXY){
10657                 wrap.setXY(wrapXY);
10658             }
10659         }
10660         return wrap;
10661     },
10662
10663         /* @private */
10664     fxUnwrap : function(wrap, pos, o){
10665         this.clearPositioning();
10666         this.setPositioning(pos);
10667         if(!o.wrap){
10668             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10669             wrap.remove();
10670         }
10671     },
10672
10673         /* @private */
10674     getFxRestore : function(){
10675         var st = this.dom.style;
10676         return {pos: this.getPositioning(), width: st.width, height : st.height};
10677     },
10678
10679         /* @private */
10680     afterFx : function(o){
10681         if(o.afterStyle){
10682             this.applyStyles(o.afterStyle);
10683         }
10684         if(o.afterCls){
10685             this.addClass(o.afterCls);
10686         }
10687         if(o.remove === true){
10688             this.remove();
10689         }
10690         Roo.callback(o.callback, o.scope, [this]);
10691         if(!o.concurrent){
10692             this.fxQueue.shift();
10693             this.nextFx();
10694         }
10695     },
10696
10697         /* @private */
10698     getFxEl : function(){ // support for composite element fx
10699         return Roo.get(this.dom);
10700     },
10701
10702         /* @private */
10703     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10704         animType = animType || 'run';
10705         opt = opt || {};
10706         var anim = Roo.lib.Anim[animType](
10707             this.dom, args,
10708             (opt.duration || defaultDur) || .35,
10709             (opt.easing || defaultEase) || 'easeOut',
10710             function(){
10711                 Roo.callback(cb, this);
10712             },
10713             this
10714         );
10715         opt.anim = anim;
10716         return anim;
10717     }
10718 };
10719
10720 // backwords compat
10721 Roo.Fx.resize = Roo.Fx.scale;
10722
10723 //When included, Roo.Fx is automatically applied to Element so that all basic
10724 //effects are available directly via the Element API
10725 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10726  * Based on:
10727  * Ext JS Library 1.1.1
10728  * Copyright(c) 2006-2007, Ext JS, LLC.
10729  *
10730  * Originally Released Under LGPL - original licence link has changed is not relivant.
10731  *
10732  * Fork - LGPL
10733  * <script type="text/javascript">
10734  */
10735
10736
10737 /**
10738  * @class Roo.CompositeElement
10739  * Standard composite class. Creates a Roo.Element for every element in the collection.
10740  * <br><br>
10741  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10742  * actions will be performed on all the elements in this collection.</b>
10743  * <br><br>
10744  * All methods return <i>this</i> and can be chained.
10745  <pre><code>
10746  var els = Roo.select("#some-el div.some-class", true);
10747  // or select directly from an existing element
10748  var el = Roo.get('some-el');
10749  el.select('div.some-class', true);
10750
10751  els.setWidth(100); // all elements become 100 width
10752  els.hide(true); // all elements fade out and hide
10753  // or
10754  els.setWidth(100).hide(true);
10755  </code></pre>
10756  */
10757 Roo.CompositeElement = function(els){
10758     this.elements = [];
10759     this.addElements(els);
10760 };
10761 Roo.CompositeElement.prototype = {
10762     isComposite: true,
10763     addElements : function(els){
10764         if(!els) return this;
10765         if(typeof els == "string"){
10766             els = Roo.Element.selectorFunction(els);
10767         }
10768         var yels = this.elements;
10769         var index = yels.length-1;
10770         for(var i = 0, len = els.length; i < len; i++) {
10771                 yels[++index] = Roo.get(els[i]);
10772         }
10773         return this;
10774     },
10775
10776     /**
10777     * Clears this composite and adds the elements returned by the passed selector.
10778     * @param {String/Array} els A string CSS selector, an array of elements or an element
10779     * @return {CompositeElement} this
10780     */
10781     fill : function(els){
10782         this.elements = [];
10783         this.add(els);
10784         return this;
10785     },
10786
10787     /**
10788     * Filters this composite to only elements that match the passed selector.
10789     * @param {String} selector A string CSS selector
10790     * @return {CompositeElement} this
10791     */
10792     filter : function(selector){
10793         var els = [];
10794         this.each(function(el){
10795             if(el.is(selector)){
10796                 els[els.length] = el.dom;
10797             }
10798         });
10799         this.fill(els);
10800         return this;
10801     },
10802
10803     invoke : function(fn, args){
10804         var els = this.elements;
10805         for(var i = 0, len = els.length; i < len; i++) {
10806                 Roo.Element.prototype[fn].apply(els[i], args);
10807         }
10808         return this;
10809     },
10810     /**
10811     * Adds elements to this composite.
10812     * @param {String/Array} els A string CSS selector, an array of elements or an element
10813     * @return {CompositeElement} this
10814     */
10815     add : function(els){
10816         if(typeof els == "string"){
10817             this.addElements(Roo.Element.selectorFunction(els));
10818         }else if(els.length !== undefined){
10819             this.addElements(els);
10820         }else{
10821             this.addElements([els]);
10822         }
10823         return this;
10824     },
10825     /**
10826     * Calls the passed function passing (el, this, index) for each element in this composite.
10827     * @param {Function} fn The function to call
10828     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10829     * @return {CompositeElement} this
10830     */
10831     each : function(fn, scope){
10832         var els = this.elements;
10833         for(var i = 0, len = els.length; i < len; i++){
10834             if(fn.call(scope || els[i], els[i], this, i) === false) {
10835                 break;
10836             }
10837         }
10838         return this;
10839     },
10840
10841     /**
10842      * Returns the Element object at the specified index
10843      * @param {Number} index
10844      * @return {Roo.Element}
10845      */
10846     item : function(index){
10847         return this.elements[index] || null;
10848     },
10849
10850     /**
10851      * Returns the first Element
10852      * @return {Roo.Element}
10853      */
10854     first : function(){
10855         return this.item(0);
10856     },
10857
10858     /**
10859      * Returns the last Element
10860      * @return {Roo.Element}
10861      */
10862     last : function(){
10863         return this.item(this.elements.length-1);
10864     },
10865
10866     /**
10867      * Returns the number of elements in this composite
10868      * @return Number
10869      */
10870     getCount : function(){
10871         return this.elements.length;
10872     },
10873
10874     /**
10875      * Returns true if this composite contains the passed element
10876      * @return Boolean
10877      */
10878     contains : function(el){
10879         return this.indexOf(el) !== -1;
10880     },
10881
10882     /**
10883      * Returns true if this composite contains the passed element
10884      * @return Boolean
10885      */
10886     indexOf : function(el){
10887         return this.elements.indexOf(Roo.get(el));
10888     },
10889
10890
10891     /**
10892     * Removes the specified element(s).
10893     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10894     * or an array of any of those.
10895     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10896     * @return {CompositeElement} this
10897     */
10898     removeElement : function(el, removeDom){
10899         if(el instanceof Array){
10900             for(var i = 0, len = el.length; i < len; i++){
10901                 this.removeElement(el[i]);
10902             }
10903             return this;
10904         }
10905         var index = typeof el == 'number' ? el : this.indexOf(el);
10906         if(index !== -1){
10907             if(removeDom){
10908                 var d = this.elements[index];
10909                 if(d.dom){
10910                     d.remove();
10911                 }else{
10912                     d.parentNode.removeChild(d);
10913                 }
10914             }
10915             this.elements.splice(index, 1);
10916         }
10917         return this;
10918     },
10919
10920     /**
10921     * Replaces the specified element with the passed element.
10922     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10923     * to replace.
10924     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10925     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10926     * @return {CompositeElement} this
10927     */
10928     replaceElement : function(el, replacement, domReplace){
10929         var index = typeof el == 'number' ? el : this.indexOf(el);
10930         if(index !== -1){
10931             if(domReplace){
10932                 this.elements[index].replaceWith(replacement);
10933             }else{
10934                 this.elements.splice(index, 1, Roo.get(replacement))
10935             }
10936         }
10937         return this;
10938     },
10939
10940     /**
10941      * Removes all elements.
10942      */
10943     clear : function(){
10944         this.elements = [];
10945     }
10946 };
10947 (function(){
10948     Roo.CompositeElement.createCall = function(proto, fnName){
10949         if(!proto[fnName]){
10950             proto[fnName] = function(){
10951                 return this.invoke(fnName, arguments);
10952             };
10953         }
10954     };
10955     for(var fnName in Roo.Element.prototype){
10956         if(typeof Roo.Element.prototype[fnName] == "function"){
10957             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10958         }
10959     };
10960 })();
10961 /*
10962  * Based on:
10963  * Ext JS Library 1.1.1
10964  * Copyright(c) 2006-2007, Ext JS, LLC.
10965  *
10966  * Originally Released Under LGPL - original licence link has changed is not relivant.
10967  *
10968  * Fork - LGPL
10969  * <script type="text/javascript">
10970  */
10971
10972 /**
10973  * @class Roo.CompositeElementLite
10974  * @extends Roo.CompositeElement
10975  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10976  <pre><code>
10977  var els = Roo.select("#some-el div.some-class");
10978  // or select directly from an existing element
10979  var el = Roo.get('some-el');
10980  el.select('div.some-class');
10981
10982  els.setWidth(100); // all elements become 100 width
10983  els.hide(true); // all elements fade out and hide
10984  // or
10985  els.setWidth(100).hide(true);
10986  </code></pre><br><br>
10987  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10988  * actions will be performed on all the elements in this collection.</b>
10989  */
10990 Roo.CompositeElementLite = function(els){
10991     Roo.CompositeElementLite.superclass.constructor.call(this, els);
10992     this.el = new Roo.Element.Flyweight();
10993 };
10994 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
10995     addElements : function(els){
10996         if(els){
10997             if(els instanceof Array){
10998                 this.elements = this.elements.concat(els);
10999             }else{
11000                 var yels = this.elements;
11001                 var index = yels.length-1;
11002                 for(var i = 0, len = els.length; i < len; i++) {
11003                     yels[++index] = els[i];
11004                 }
11005             }
11006         }
11007         return this;
11008     },
11009     invoke : function(fn, args){
11010         var els = this.elements;
11011         var el = this.el;
11012         for(var i = 0, len = els.length; i < len; i++) {
11013             el.dom = els[i];
11014                 Roo.Element.prototype[fn].apply(el, args);
11015         }
11016         return this;
11017     },
11018     /**
11019      * Returns a flyweight Element of the dom element object at the specified index
11020      * @param {Number} index
11021      * @return {Roo.Element}
11022      */
11023     item : function(index){
11024         if(!this.elements[index]){
11025             return null;
11026         }
11027         this.el.dom = this.elements[index];
11028         return this.el;
11029     },
11030
11031     // fixes scope with flyweight
11032     addListener : function(eventName, handler, scope, opt){
11033         var els = this.elements;
11034         for(var i = 0, len = els.length; i < len; i++) {
11035             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11036         }
11037         return this;
11038     },
11039
11040     /**
11041     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11042     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11043     * a reference to the dom node, use el.dom.</b>
11044     * @param {Function} fn The function to call
11045     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11046     * @return {CompositeElement} this
11047     */
11048     each : function(fn, scope){
11049         var els = this.elements;
11050         var el = this.el;
11051         for(var i = 0, len = els.length; i < len; i++){
11052             el.dom = els[i];
11053                 if(fn.call(scope || el, el, this, i) === false){
11054                 break;
11055             }
11056         }
11057         return this;
11058     },
11059
11060     indexOf : function(el){
11061         return this.elements.indexOf(Roo.getDom(el));
11062     },
11063
11064     replaceElement : function(el, replacement, domReplace){
11065         var index = typeof el == 'number' ? el : this.indexOf(el);
11066         if(index !== -1){
11067             replacement = Roo.getDom(replacement);
11068             if(domReplace){
11069                 var d = this.elements[index];
11070                 d.parentNode.insertBefore(replacement, d);
11071                 d.parentNode.removeChild(d);
11072             }
11073             this.elements.splice(index, 1, replacement);
11074         }
11075         return this;
11076     }
11077 });
11078 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11079
11080 /*
11081  * Based on:
11082  * Ext JS Library 1.1.1
11083  * Copyright(c) 2006-2007, Ext JS, LLC.
11084  *
11085  * Originally Released Under LGPL - original licence link has changed is not relivant.
11086  *
11087  * Fork - LGPL
11088  * <script type="text/javascript">
11089  */
11090
11091  
11092
11093 /**
11094  * @class Roo.data.Connection
11095  * @extends Roo.util.Observable
11096  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11097  * either to a configured URL, or to a URL specified at request time.<br><br>
11098  * <p>
11099  * Requests made by this class are asynchronous, and will return immediately. No data from
11100  * the server will be available to the statement immediately following the {@link #request} call.
11101  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11102  * <p>
11103  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11104  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11105  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11106  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11107  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11108  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11109  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11110  * standard DOM methods.
11111  * @constructor
11112  * @param {Object} config a configuration object.
11113  */
11114 Roo.data.Connection = function(config){
11115     Roo.apply(this, config);
11116     this.addEvents({
11117         /**
11118          * @event beforerequest
11119          * Fires before a network request is made to retrieve a data object.
11120          * @param {Connection} conn This Connection object.
11121          * @param {Object} options The options config object passed to the {@link #request} method.
11122          */
11123         "beforerequest" : true,
11124         /**
11125          * @event requestcomplete
11126          * Fires if the request was successfully completed.
11127          * @param {Connection} conn This Connection object.
11128          * @param {Object} response The XHR object containing the response data.
11129          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11130          * @param {Object} options The options config object passed to the {@link #request} method.
11131          */
11132         "requestcomplete" : true,
11133         /**
11134          * @event requestexception
11135          * Fires if an error HTTP status was returned from the server.
11136          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11137          * @param {Connection} conn This Connection object.
11138          * @param {Object} response The XHR object containing the response data.
11139          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11140          * @param {Object} options The options config object passed to the {@link #request} method.
11141          */
11142         "requestexception" : true
11143     });
11144     Roo.data.Connection.superclass.constructor.call(this);
11145 };
11146
11147 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11148     /**
11149      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11150      */
11151     /**
11152      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11153      * extra parameters to each request made by this object. (defaults to undefined)
11154      */
11155     /**
11156      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11157      *  to each request made by this object. (defaults to undefined)
11158      */
11159     /**
11160      * @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)
11161      */
11162     /**
11163      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11164      */
11165     timeout : 30000,
11166     /**
11167      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11168      * @type Boolean
11169      */
11170     autoAbort:false,
11171
11172     /**
11173      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11174      * @type Boolean
11175      */
11176     disableCaching: true,
11177
11178     /**
11179      * Sends an HTTP request to a remote server.
11180      * @param {Object} options An object which may contain the following properties:<ul>
11181      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11182      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11183      * request, a url encoded string or a function to call to get either.</li>
11184      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11185      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11186      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11187      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11188      * <li>options {Object} The parameter to the request call.</li>
11189      * <li>success {Boolean} True if the request succeeded.</li>
11190      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11191      * </ul></li>
11192      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11193      * The callback is passed the following parameters:<ul>
11194      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11195      * <li>options {Object} The parameter to the request call.</li>
11196      * </ul></li>
11197      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11198      * The callback is passed the following parameters:<ul>
11199      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11200      * <li>options {Object} The parameter to the request call.</li>
11201      * </ul></li>
11202      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11203      * for the callback function. Defaults to the browser window.</li>
11204      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11205      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11206      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11207      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11208      * params for the post data. Any params will be appended to the URL.</li>
11209      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11210      * </ul>
11211      * @return {Number} transactionId
11212      */
11213     request : function(o){
11214         if(this.fireEvent("beforerequest", this, o) !== false){
11215             var p = o.params;
11216
11217             if(typeof p == "function"){
11218                 p = p.call(o.scope||window, o);
11219             }
11220             if(typeof p == "object"){
11221                 p = Roo.urlEncode(o.params);
11222             }
11223             if(this.extraParams){
11224                 var extras = Roo.urlEncode(this.extraParams);
11225                 p = p ? (p + '&' + extras) : extras;
11226             }
11227
11228             var url = o.url || this.url;
11229             if(typeof url == 'function'){
11230                 url = url.call(o.scope||window, o);
11231             }
11232
11233             if(o.form){
11234                 var form = Roo.getDom(o.form);
11235                 url = url || form.action;
11236
11237                 var enctype = form.getAttribute("enctype");
11238                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11239                     return this.doFormUpload(o, p, url);
11240                 }
11241                 var f = Roo.lib.Ajax.serializeForm(form);
11242                 p = p ? (p + '&' + f) : f;
11243             }
11244
11245             var hs = o.headers;
11246             if(this.defaultHeaders){
11247                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11248                 if(!o.headers){
11249                     o.headers = hs;
11250                 }
11251             }
11252
11253             var cb = {
11254                 success: this.handleResponse,
11255                 failure: this.handleFailure,
11256                 scope: this,
11257                 argument: {options: o},
11258                 timeout : this.timeout
11259             };
11260
11261             var method = o.method||this.method||(p ? "POST" : "GET");
11262
11263             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11264                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11265             }
11266
11267             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11268                 if(o.autoAbort){
11269                     this.abort();
11270                 }
11271             }else if(this.autoAbort !== false){
11272                 this.abort();
11273             }
11274
11275             if((method == 'GET' && p) || o.xmlData){
11276                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11277                 p = '';
11278             }
11279             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11280             return this.transId;
11281         }else{
11282             Roo.callback(o.callback, o.scope, [o, null, null]);
11283             return null;
11284         }
11285     },
11286
11287     /**
11288      * Determine whether this object has a request outstanding.
11289      * @param {Number} transactionId (Optional) defaults to the last transaction
11290      * @return {Boolean} True if there is an outstanding request.
11291      */
11292     isLoading : function(transId){
11293         if(transId){
11294             return Roo.lib.Ajax.isCallInProgress(transId);
11295         }else{
11296             return this.transId ? true : false;
11297         }
11298     },
11299
11300     /**
11301      * Aborts any outstanding request.
11302      * @param {Number} transactionId (Optional) defaults to the last transaction
11303      */
11304     abort : function(transId){
11305         if(transId || this.isLoading()){
11306             Roo.lib.Ajax.abort(transId || this.transId);
11307         }
11308     },
11309
11310     // private
11311     handleResponse : function(response){
11312         this.transId = false;
11313         var options = response.argument.options;
11314         response.argument = options ? options.argument : null;
11315         this.fireEvent("requestcomplete", this, response, options);
11316         Roo.callback(options.success, options.scope, [response, options]);
11317         Roo.callback(options.callback, options.scope, [options, true, response]);
11318     },
11319
11320     // private
11321     handleFailure : function(response, e){
11322         this.transId = false;
11323         var options = response.argument.options;
11324         response.argument = options ? options.argument : null;
11325         this.fireEvent("requestexception", this, response, options, e);
11326         Roo.callback(options.failure, options.scope, [response, options]);
11327         Roo.callback(options.callback, options.scope, [options, false, response]);
11328     },
11329
11330     // private
11331     doFormUpload : function(o, ps, url){
11332         var id = Roo.id();
11333         var frame = document.createElement('iframe');
11334         frame.id = id;
11335         frame.name = id;
11336         frame.className = 'x-hidden';
11337         if(Roo.isIE){
11338             frame.src = Roo.SSL_SECURE_URL;
11339         }
11340         document.body.appendChild(frame);
11341
11342         if(Roo.isIE){
11343            document.frames[id].name = id;
11344         }
11345
11346         var form = Roo.getDom(o.form);
11347         form.target = id;
11348         form.method = 'POST';
11349         form.enctype = form.encoding = 'multipart/form-data';
11350         if(url){
11351             form.action = url;
11352         }
11353
11354         var hiddens, hd;
11355         if(ps){ // add dynamic params
11356             hiddens = [];
11357             ps = Roo.urlDecode(ps, false);
11358             for(var k in ps){
11359                 if(ps.hasOwnProperty(k)){
11360                     hd = document.createElement('input');
11361                     hd.type = 'hidden';
11362                     hd.name = k;
11363                     hd.value = ps[k];
11364                     form.appendChild(hd);
11365                     hiddens.push(hd);
11366                 }
11367             }
11368         }
11369
11370         function cb(){
11371             var r = {  // bogus response object
11372                 responseText : '',
11373                 responseXML : null
11374             };
11375
11376             r.argument = o ? o.argument : null;
11377
11378             try { //
11379                 var doc;
11380                 if(Roo.isIE){
11381                     doc = frame.contentWindow.document;
11382                 }else {
11383                     doc = (frame.contentDocument || window.frames[id].document);
11384                 }
11385                 if(doc && doc.body){
11386                     r.responseText = doc.body.innerHTML;
11387                 }
11388                 if(doc && doc.XMLDocument){
11389                     r.responseXML = doc.XMLDocument;
11390                 }else {
11391                     r.responseXML = doc;
11392                 }
11393             }
11394             catch(e) {
11395                 // ignore
11396             }
11397
11398             Roo.EventManager.removeListener(frame, 'load', cb, this);
11399
11400             this.fireEvent("requestcomplete", this, r, o);
11401             Roo.callback(o.success, o.scope, [r, o]);
11402             Roo.callback(o.callback, o.scope, [o, true, r]);
11403
11404             setTimeout(function(){document.body.removeChild(frame);}, 100);
11405         }
11406
11407         Roo.EventManager.on(frame, 'load', cb, this);
11408         form.submit();
11409
11410         if(hiddens){ // remove dynamic params
11411             for(var i = 0, len = hiddens.length; i < len; i++){
11412                 form.removeChild(hiddens[i]);
11413             }
11414         }
11415     }
11416 });
11417
11418 /**
11419  * @class Roo.Ajax
11420  * @extends Roo.data.Connection
11421  * Global Ajax request class.
11422  *
11423  * @singleton
11424  */
11425 Roo.Ajax = new Roo.data.Connection({
11426     // fix up the docs
11427    /**
11428      * @cfg {String} url @hide
11429      */
11430     /**
11431      * @cfg {Object} extraParams @hide
11432      */
11433     /**
11434      * @cfg {Object} defaultHeaders @hide
11435      */
11436     /**
11437      * @cfg {String} method (Optional) @hide
11438      */
11439     /**
11440      * @cfg {Number} timeout (Optional) @hide
11441      */
11442     /**
11443      * @cfg {Boolean} autoAbort (Optional) @hide
11444      */
11445
11446     /**
11447      * @cfg {Boolean} disableCaching (Optional) @hide
11448      */
11449
11450     /**
11451      * @property  disableCaching
11452      * True to add a unique cache-buster param to GET requests. (defaults to true)
11453      * @type Boolean
11454      */
11455     /**
11456      * @property  url
11457      * The default URL to be used for requests to the server. (defaults to undefined)
11458      * @type String
11459      */
11460     /**
11461      * @property  extraParams
11462      * An object containing properties which are used as
11463      * extra parameters to each request made by this object. (defaults to undefined)
11464      * @type Object
11465      */
11466     /**
11467      * @property  defaultHeaders
11468      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11469      * @type Object
11470      */
11471     /**
11472      * @property  method
11473      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11474      * @type String
11475      */
11476     /**
11477      * @property  timeout
11478      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11479      * @type Number
11480      */
11481
11482     /**
11483      * @property  autoAbort
11484      * Whether a new request should abort any pending requests. (defaults to false)
11485      * @type Boolean
11486      */
11487     autoAbort : false,
11488
11489     /**
11490      * Serialize the passed form into a url encoded string
11491      * @param {String/HTMLElement} form
11492      * @return {String}
11493      */
11494     serializeForm : function(form){
11495         return Roo.lib.Ajax.serializeForm(form);
11496     }
11497 });/*
11498  * Based on:
11499  * Ext JS Library 1.1.1
11500  * Copyright(c) 2006-2007, Ext JS, LLC.
11501  *
11502  * Originally Released Under LGPL - original licence link has changed is not relivant.
11503  *
11504  * Fork - LGPL
11505  * <script type="text/javascript">
11506  */
11507  
11508 /**
11509  * @class Roo.Ajax
11510  * @extends Roo.data.Connection
11511  * Global Ajax request class.
11512  *
11513  * @instanceOf  Roo.data.Connection
11514  */
11515 Roo.Ajax = new Roo.data.Connection({
11516     // fix up the docs
11517     
11518     /**
11519      * fix up scoping
11520      * @scope Roo.Ajax
11521      */
11522     
11523    /**
11524      * @cfg {String} url @hide
11525      */
11526     /**
11527      * @cfg {Object} extraParams @hide
11528      */
11529     /**
11530      * @cfg {Object} defaultHeaders @hide
11531      */
11532     /**
11533      * @cfg {String} method (Optional) @hide
11534      */
11535     /**
11536      * @cfg {Number} timeout (Optional) @hide
11537      */
11538     /**
11539      * @cfg {Boolean} autoAbort (Optional) @hide
11540      */
11541
11542     /**
11543      * @cfg {Boolean} disableCaching (Optional) @hide
11544      */
11545
11546     /**
11547      * @property  disableCaching
11548      * True to add a unique cache-buster param to GET requests. (defaults to true)
11549      * @type Boolean
11550      */
11551     /**
11552      * @property  url
11553      * The default URL to be used for requests to the server. (defaults to undefined)
11554      * @type String
11555      */
11556     /**
11557      * @property  extraParams
11558      * An object containing properties which are used as
11559      * extra parameters to each request made by this object. (defaults to undefined)
11560      * @type Object
11561      */
11562     /**
11563      * @property  defaultHeaders
11564      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11565      * @type Object
11566      */
11567     /**
11568      * @property  method
11569      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11570      * @type String
11571      */
11572     /**
11573      * @property  timeout
11574      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11575      * @type Number
11576      */
11577
11578     /**
11579      * @property  autoAbort
11580      * Whether a new request should abort any pending requests. (defaults to false)
11581      * @type Boolean
11582      */
11583     autoAbort : false,
11584
11585     /**
11586      * Serialize the passed form into a url encoded string
11587      * @param {String/HTMLElement} form
11588      * @return {String}
11589      */
11590     serializeForm : function(form){
11591         return Roo.lib.Ajax.serializeForm(form);
11592     }
11593 });/*
11594  * Based on:
11595  * Ext JS Library 1.1.1
11596  * Copyright(c) 2006-2007, Ext JS, LLC.
11597  *
11598  * Originally Released Under LGPL - original licence link has changed is not relivant.
11599  *
11600  * Fork - LGPL
11601  * <script type="text/javascript">
11602  */
11603
11604  
11605 /**
11606  * @class Roo.UpdateManager
11607  * @extends Roo.util.Observable
11608  * Provides AJAX-style update for Element object.<br><br>
11609  * Usage:<br>
11610  * <pre><code>
11611  * // Get it from a Roo.Element object
11612  * var el = Roo.get("foo");
11613  * var mgr = el.getUpdateManager();
11614  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11615  * ...
11616  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11617  * <br>
11618  * // or directly (returns the same UpdateManager instance)
11619  * var mgr = new Roo.UpdateManager("myElementId");
11620  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11621  * mgr.on("update", myFcnNeedsToKnow);
11622  * <br>
11623    // short handed call directly from the element object
11624    Roo.get("foo").load({
11625         url: "bar.php",
11626         scripts:true,
11627         params: "for=bar",
11628         text: "Loading Foo..."
11629    });
11630  * </code></pre>
11631  * @constructor
11632  * Create new UpdateManager directly.
11633  * @param {String/HTMLElement/Roo.Element} el The element to update
11634  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11635  */
11636 Roo.UpdateManager = function(el, forceNew){
11637     el = Roo.get(el);
11638     if(!forceNew && el.updateManager){
11639         return el.updateManager;
11640     }
11641     /**
11642      * The Element object
11643      * @type Roo.Element
11644      */
11645     this.el = el;
11646     /**
11647      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11648      * @type String
11649      */
11650     this.defaultUrl = null;
11651
11652     this.addEvents({
11653         /**
11654          * @event beforeupdate
11655          * Fired before an update is made, return false from your handler and the update is cancelled.
11656          * @param {Roo.Element} el
11657          * @param {String/Object/Function} url
11658          * @param {String/Object} params
11659          */
11660         "beforeupdate": true,
11661         /**
11662          * @event update
11663          * Fired after successful update is made.
11664          * @param {Roo.Element} el
11665          * @param {Object} oResponseObject The response Object
11666          */
11667         "update": true,
11668         /**
11669          * @event failure
11670          * Fired on update failure.
11671          * @param {Roo.Element} el
11672          * @param {Object} oResponseObject The response Object
11673          */
11674         "failure": true
11675     });
11676     var d = Roo.UpdateManager.defaults;
11677     /**
11678      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11679      * @type String
11680      */
11681     this.sslBlankUrl = d.sslBlankUrl;
11682     /**
11683      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11684      * @type Boolean
11685      */
11686     this.disableCaching = d.disableCaching;
11687     /**
11688      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11689      * @type String
11690      */
11691     this.indicatorText = d.indicatorText;
11692     /**
11693      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11694      * @type String
11695      */
11696     this.showLoadIndicator = d.showLoadIndicator;
11697     /**
11698      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11699      * @type Number
11700      */
11701     this.timeout = d.timeout;
11702
11703     /**
11704      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11705      * @type Boolean
11706      */
11707     this.loadScripts = d.loadScripts;
11708
11709     /**
11710      * Transaction object of current executing transaction
11711      */
11712     this.transaction = null;
11713
11714     /**
11715      * @private
11716      */
11717     this.autoRefreshProcId = null;
11718     /**
11719      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11720      * @type Function
11721      */
11722     this.refreshDelegate = this.refresh.createDelegate(this);
11723     /**
11724      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11725      * @type Function
11726      */
11727     this.updateDelegate = this.update.createDelegate(this);
11728     /**
11729      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11730      * @type Function
11731      */
11732     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11733     /**
11734      * @private
11735      */
11736     this.successDelegate = this.processSuccess.createDelegate(this);
11737     /**
11738      * @private
11739      */
11740     this.failureDelegate = this.processFailure.createDelegate(this);
11741
11742     if(!this.renderer){
11743      /**
11744       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11745       */
11746     this.renderer = new Roo.UpdateManager.BasicRenderer();
11747     }
11748     
11749     Roo.UpdateManager.superclass.constructor.call(this);
11750 };
11751
11752 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11753     /**
11754      * Get the Element this UpdateManager is bound to
11755      * @return {Roo.Element} The element
11756      */
11757     getEl : function(){
11758         return this.el;
11759     },
11760     /**
11761      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11762      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11763 <pre><code>
11764 um.update({<br/>
11765     url: "your-url.php",<br/>
11766     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11767     callback: yourFunction,<br/>
11768     scope: yourObject, //(optional scope)  <br/>
11769     discardUrl: false, <br/>
11770     nocache: false,<br/>
11771     text: "Loading...",<br/>
11772     timeout: 30,<br/>
11773     scripts: false<br/>
11774 });
11775 </code></pre>
11776      * The only required property is url. The optional properties nocache, text and scripts
11777      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11778      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11779      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11780      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11781      */
11782     update : function(url, params, callback, discardUrl){
11783         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11784             var method = this.method, cfg;
11785             if(typeof url == "object"){ // must be config object
11786                 cfg = url;
11787                 url = cfg.url;
11788                 params = params || cfg.params;
11789                 callback = callback || cfg.callback;
11790                 discardUrl = discardUrl || cfg.discardUrl;
11791                 if(callback && cfg.scope){
11792                     callback = callback.createDelegate(cfg.scope);
11793                 }
11794                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11795                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11796                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11797                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11798                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11799             }
11800             this.showLoading();
11801             if(!discardUrl){
11802                 this.defaultUrl = url;
11803             }
11804             if(typeof url == "function"){
11805                 url = url.call(this);
11806             }
11807
11808             method = method || (params ? "POST" : "GET");
11809             if(method == "GET"){
11810                 url = this.prepareUrl(url);
11811             }
11812
11813             var o = Roo.apply(cfg ||{}, {
11814                 url : url,
11815                 params: params,
11816                 success: this.successDelegate,
11817                 failure: this.failureDelegate,
11818                 callback: undefined,
11819                 timeout: (this.timeout*1000),
11820                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11821             });
11822
11823             this.transaction = Roo.Ajax.request(o);
11824         }
11825     },
11826
11827     /**
11828      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11829      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11830      * @param {String/HTMLElement} form The form Id or form element
11831      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11832      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11833      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11834      */
11835     formUpdate : function(form, url, reset, callback){
11836         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11837             if(typeof url == "function"){
11838                 url = url.call(this);
11839             }
11840             form = Roo.getDom(form);
11841             this.transaction = Roo.Ajax.request({
11842                 form: form,
11843                 url:url,
11844                 success: this.successDelegate,
11845                 failure: this.failureDelegate,
11846                 timeout: (this.timeout*1000),
11847                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11848             });
11849             this.showLoading.defer(1, this);
11850         }
11851     },
11852
11853     /**
11854      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11855      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11856      */
11857     refresh : function(callback){
11858         if(this.defaultUrl == null){
11859             return;
11860         }
11861         this.update(this.defaultUrl, null, callback, true);
11862     },
11863
11864     /**
11865      * Set this element to auto refresh.
11866      * @param {Number} interval How often to update (in seconds).
11867      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11868      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11869      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11870      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11871      */
11872     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11873         if(refreshNow){
11874             this.update(url || this.defaultUrl, params, callback, true);
11875         }
11876         if(this.autoRefreshProcId){
11877             clearInterval(this.autoRefreshProcId);
11878         }
11879         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11880     },
11881
11882     /**
11883      * Stop auto refresh on this element.
11884      */
11885      stopAutoRefresh : function(){
11886         if(this.autoRefreshProcId){
11887             clearInterval(this.autoRefreshProcId);
11888             delete this.autoRefreshProcId;
11889         }
11890     },
11891
11892     isAutoRefreshing : function(){
11893        return this.autoRefreshProcId ? true : false;
11894     },
11895     /**
11896      * Called to update the element to "Loading" state. Override to perform custom action.
11897      */
11898     showLoading : function(){
11899         if(this.showLoadIndicator){
11900             this.el.update(this.indicatorText);
11901         }
11902     },
11903
11904     /**
11905      * Adds unique parameter to query string if disableCaching = true
11906      * @private
11907      */
11908     prepareUrl : function(url){
11909         if(this.disableCaching){
11910             var append = "_dc=" + (new Date().getTime());
11911             if(url.indexOf("?") !== -1){
11912                 url += "&" + append;
11913             }else{
11914                 url += "?" + append;
11915             }
11916         }
11917         return url;
11918     },
11919
11920     /**
11921      * @private
11922      */
11923     processSuccess : function(response){
11924         this.transaction = null;
11925         if(response.argument.form && response.argument.reset){
11926             try{ // put in try/catch since some older FF releases had problems with this
11927                 response.argument.form.reset();
11928             }catch(e){}
11929         }
11930         if(this.loadScripts){
11931             this.renderer.render(this.el, response, this,
11932                 this.updateComplete.createDelegate(this, [response]));
11933         }else{
11934             this.renderer.render(this.el, response, this);
11935             this.updateComplete(response);
11936         }
11937     },
11938
11939     updateComplete : function(response){
11940         this.fireEvent("update", this.el, response);
11941         if(typeof response.argument.callback == "function"){
11942             response.argument.callback(this.el, true, response);
11943         }
11944     },
11945
11946     /**
11947      * @private
11948      */
11949     processFailure : function(response){
11950         this.transaction = null;
11951         this.fireEvent("failure", this.el, response);
11952         if(typeof response.argument.callback == "function"){
11953             response.argument.callback(this.el, false, response);
11954         }
11955     },
11956
11957     /**
11958      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11959      * @param {Object} renderer The object implementing the render() method
11960      */
11961     setRenderer : function(renderer){
11962         this.renderer = renderer;
11963     },
11964
11965     getRenderer : function(){
11966        return this.renderer;
11967     },
11968
11969     /**
11970      * Set the defaultUrl used for updates
11971      * @param {String/Function} defaultUrl The url or a function to call to get the url
11972      */
11973     setDefaultUrl : function(defaultUrl){
11974         this.defaultUrl = defaultUrl;
11975     },
11976
11977     /**
11978      * Aborts the executing transaction
11979      */
11980     abort : function(){
11981         if(this.transaction){
11982             Roo.Ajax.abort(this.transaction);
11983         }
11984     },
11985
11986     /**
11987      * Returns true if an update is in progress
11988      * @return {Boolean}
11989      */
11990     isUpdating : function(){
11991         if(this.transaction){
11992             return Roo.Ajax.isLoading(this.transaction);
11993         }
11994         return false;
11995     }
11996 });
11997
11998 /**
11999  * @class Roo.UpdateManager.defaults
12000  * @static (not really - but it helps the doc tool)
12001  * The defaults collection enables customizing the default properties of UpdateManager
12002  */
12003    Roo.UpdateManager.defaults = {
12004        /**
12005          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12006          * @type Number
12007          */
12008          timeout : 30,
12009
12010          /**
12011          * True to process scripts by default (Defaults to false).
12012          * @type Boolean
12013          */
12014         loadScripts : false,
12015
12016         /**
12017         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12018         * @type String
12019         */
12020         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12021         /**
12022          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12023          * @type Boolean
12024          */
12025         disableCaching : false,
12026         /**
12027          * Whether to show indicatorText when loading (Defaults to true).
12028          * @type Boolean
12029          */
12030         showLoadIndicator : true,
12031         /**
12032          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12033          * @type String
12034          */
12035         indicatorText : '<div class="loading-indicator">Loading...</div>'
12036    };
12037
12038 /**
12039  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12040  *Usage:
12041  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12042  * @param {String/HTMLElement/Roo.Element} el The element to update
12043  * @param {String} url The url
12044  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12045  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12046  * @static
12047  * @deprecated
12048  * @member Roo.UpdateManager
12049  */
12050 Roo.UpdateManager.updateElement = function(el, url, params, options){
12051     var um = Roo.get(el, true).getUpdateManager();
12052     Roo.apply(um, options);
12053     um.update(url, params, options ? options.callback : null);
12054 };
12055 // alias for backwards compat
12056 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12057 /**
12058  * @class Roo.UpdateManager.BasicRenderer
12059  * Default Content renderer. Updates the elements innerHTML with the responseText.
12060  */
12061 Roo.UpdateManager.BasicRenderer = function(){};
12062
12063 Roo.UpdateManager.BasicRenderer.prototype = {
12064     /**
12065      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12066      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12067      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12068      * @param {Roo.Element} el The element being rendered
12069      * @param {Object} response The YUI Connect response object
12070      * @param {UpdateManager} updateManager The calling update manager
12071      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12072      */
12073      render : function(el, response, updateManager, callback){
12074         el.update(response.responseText, updateManager.loadScripts, callback);
12075     }
12076 };
12077 /*
12078  * Based on:
12079  * Ext JS Library 1.1.1
12080  * Copyright(c) 2006-2007, Ext JS, LLC.
12081  *
12082  * Originally Released Under LGPL - original licence link has changed is not relivant.
12083  *
12084  * Fork - LGPL
12085  * <script type="text/javascript">
12086  */
12087
12088 /**
12089  * @class Roo.util.DelayedTask
12090  * Provides a convenient method of performing setTimeout where a new
12091  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12092  * You can use this class to buffer
12093  * the keypress events for a certain number of milliseconds, and perform only if they stop
12094  * for that amount of time.
12095  * @constructor The parameters to this constructor serve as defaults and are not required.
12096  * @param {Function} fn (optional) The default function to timeout
12097  * @param {Object} scope (optional) The default scope of that timeout
12098  * @param {Array} args (optional) The default Array of arguments
12099  */
12100 Roo.util.DelayedTask = function(fn, scope, args){
12101     var id = null, d, t;
12102
12103     var call = function(){
12104         var now = new Date().getTime();
12105         if(now - t >= d){
12106             clearInterval(id);
12107             id = null;
12108             fn.apply(scope, args || []);
12109         }
12110     };
12111     /**
12112      * Cancels any pending timeout and queues a new one
12113      * @param {Number} delay The milliseconds to delay
12114      * @param {Function} newFn (optional) Overrides function passed to constructor
12115      * @param {Object} newScope (optional) Overrides scope passed to constructor
12116      * @param {Array} newArgs (optional) Overrides args passed to constructor
12117      */
12118     this.delay = function(delay, newFn, newScope, newArgs){
12119         if(id && delay != d){
12120             this.cancel();
12121         }
12122         d = delay;
12123         t = new Date().getTime();
12124         fn = newFn || fn;
12125         scope = newScope || scope;
12126         args = newArgs || args;
12127         if(!id){
12128             id = setInterval(call, d);
12129         }
12130     };
12131
12132     /**
12133      * Cancel the last queued timeout
12134      */
12135     this.cancel = function(){
12136         if(id){
12137             clearInterval(id);
12138             id = null;
12139         }
12140     };
12141 };/*
12142  * Based on:
12143  * Ext JS Library 1.1.1
12144  * Copyright(c) 2006-2007, Ext JS, LLC.
12145  *
12146  * Originally Released Under LGPL - original licence link has changed is not relivant.
12147  *
12148  * Fork - LGPL
12149  * <script type="text/javascript">
12150  */
12151  
12152  
12153 Roo.util.TaskRunner = function(interval){
12154     interval = interval || 10;
12155     var tasks = [], removeQueue = [];
12156     var id = 0;
12157     var running = false;
12158
12159     var stopThread = function(){
12160         running = false;
12161         clearInterval(id);
12162         id = 0;
12163     };
12164
12165     var startThread = function(){
12166         if(!running){
12167             running = true;
12168             id = setInterval(runTasks, interval);
12169         }
12170     };
12171
12172     var removeTask = function(task){
12173         removeQueue.push(task);
12174         if(task.onStop){
12175             task.onStop();
12176         }
12177     };
12178
12179     var runTasks = function(){
12180         if(removeQueue.length > 0){
12181             for(var i = 0, len = removeQueue.length; i < len; i++){
12182                 tasks.remove(removeQueue[i]);
12183             }
12184             removeQueue = [];
12185             if(tasks.length < 1){
12186                 stopThread();
12187                 return;
12188             }
12189         }
12190         var now = new Date().getTime();
12191         for(var i = 0, len = tasks.length; i < len; ++i){
12192             var t = tasks[i];
12193             var itime = now - t.taskRunTime;
12194             if(t.interval <= itime){
12195                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12196                 t.taskRunTime = now;
12197                 if(rt === false || t.taskRunCount === t.repeat){
12198                     removeTask(t);
12199                     return;
12200                 }
12201             }
12202             if(t.duration && t.duration <= (now - t.taskStartTime)){
12203                 removeTask(t);
12204             }
12205         }
12206     };
12207
12208     /**
12209      * Queues a new task.
12210      * @param {Object} task
12211      */
12212     this.start = function(task){
12213         tasks.push(task);
12214         task.taskStartTime = new Date().getTime();
12215         task.taskRunTime = 0;
12216         task.taskRunCount = 0;
12217         startThread();
12218         return task;
12219     };
12220
12221     this.stop = function(task){
12222         removeTask(task);
12223         return task;
12224     };
12225
12226     this.stopAll = function(){
12227         stopThread();
12228         for(var i = 0, len = tasks.length; i < len; i++){
12229             if(tasks[i].onStop){
12230                 tasks[i].onStop();
12231             }
12232         }
12233         tasks = [];
12234         removeQueue = [];
12235     };
12236 };
12237
12238 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12239  * Based on:
12240  * Ext JS Library 1.1.1
12241  * Copyright(c) 2006-2007, Ext JS, LLC.
12242  *
12243  * Originally Released Under LGPL - original licence link has changed is not relivant.
12244  *
12245  * Fork - LGPL
12246  * <script type="text/javascript">
12247  */
12248
12249  
12250 /**
12251  * @class Roo.util.MixedCollection
12252  * @extends Roo.util.Observable
12253  * A Collection class that maintains both numeric indexes and keys and exposes events.
12254  * @constructor
12255  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12256  * collection (defaults to false)
12257  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12258  * and return the key value for that item.  This is used when available to look up the key on items that
12259  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12260  * equivalent to providing an implementation for the {@link #getKey} method.
12261  */
12262 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12263     this.items = [];
12264     this.map = {};
12265     this.keys = [];
12266     this.length = 0;
12267     this.addEvents({
12268         /**
12269          * @event clear
12270          * Fires when the collection is cleared.
12271          */
12272         "clear" : true,
12273         /**
12274          * @event add
12275          * Fires when an item is added to the collection.
12276          * @param {Number} index The index at which the item was added.
12277          * @param {Object} o The item added.
12278          * @param {String} key The key associated with the added item.
12279          */
12280         "add" : true,
12281         /**
12282          * @event replace
12283          * Fires when an item is replaced in the collection.
12284          * @param {String} key he key associated with the new added.
12285          * @param {Object} old The item being replaced.
12286          * @param {Object} new The new item.
12287          */
12288         "replace" : true,
12289         /**
12290          * @event remove
12291          * Fires when an item is removed from the collection.
12292          * @param {Object} o The item being removed.
12293          * @param {String} key (optional) The key associated with the removed item.
12294          */
12295         "remove" : true,
12296         "sort" : true
12297     });
12298     this.allowFunctions = allowFunctions === true;
12299     if(keyFn){
12300         this.getKey = keyFn;
12301     }
12302     Roo.util.MixedCollection.superclass.constructor.call(this);
12303 };
12304
12305 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12306     allowFunctions : false,
12307     
12308 /**
12309  * Adds an item to the collection.
12310  * @param {String} key The key to associate with the item
12311  * @param {Object} o The item to add.
12312  * @return {Object} The item added.
12313  */
12314     add : function(key, o){
12315         if(arguments.length == 1){
12316             o = arguments[0];
12317             key = this.getKey(o);
12318         }
12319         if(typeof key == "undefined" || key === null){
12320             this.length++;
12321             this.items.push(o);
12322             this.keys.push(null);
12323         }else{
12324             var old = this.map[key];
12325             if(old){
12326                 return this.replace(key, o);
12327             }
12328             this.length++;
12329             this.items.push(o);
12330             this.map[key] = o;
12331             this.keys.push(key);
12332         }
12333         this.fireEvent("add", this.length-1, o, key);
12334         return o;
12335     },
12336        
12337 /**
12338   * MixedCollection has a generic way to fetch keys if you implement getKey.
12339 <pre><code>
12340 // normal way
12341 var mc = new Roo.util.MixedCollection();
12342 mc.add(someEl.dom.id, someEl);
12343 mc.add(otherEl.dom.id, otherEl);
12344 //and so on
12345
12346 // using getKey
12347 var mc = new Roo.util.MixedCollection();
12348 mc.getKey = function(el){
12349    return el.dom.id;
12350 };
12351 mc.add(someEl);
12352 mc.add(otherEl);
12353
12354 // or via the constructor
12355 var mc = new Roo.util.MixedCollection(false, function(el){
12356    return el.dom.id;
12357 });
12358 mc.add(someEl);
12359 mc.add(otherEl);
12360 </code></pre>
12361  * @param o {Object} The item for which to find the key.
12362  * @return {Object} The key for the passed item.
12363  */
12364     getKey : function(o){
12365          return o.id; 
12366     },
12367    
12368 /**
12369  * Replaces an item in the collection.
12370  * @param {String} key The key associated with the item to replace, or the item to replace.
12371  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12372  * @return {Object}  The new item.
12373  */
12374     replace : function(key, o){
12375         if(arguments.length == 1){
12376             o = arguments[0];
12377             key = this.getKey(o);
12378         }
12379         var old = this.item(key);
12380         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12381              return this.add(key, o);
12382         }
12383         var index = this.indexOfKey(key);
12384         this.items[index] = o;
12385         this.map[key] = o;
12386         this.fireEvent("replace", key, old, o);
12387         return o;
12388     },
12389    
12390 /**
12391  * Adds all elements of an Array or an Object to the collection.
12392  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12393  * an Array of values, each of which are added to the collection.
12394  */
12395     addAll : function(objs){
12396         if(arguments.length > 1 || objs instanceof Array){
12397             var args = arguments.length > 1 ? arguments : objs;
12398             for(var i = 0, len = args.length; i < len; i++){
12399                 this.add(args[i]);
12400             }
12401         }else{
12402             for(var key in objs){
12403                 if(this.allowFunctions || typeof objs[key] != "function"){
12404                     this.add(key, objs[key]);
12405                 }
12406             }
12407         }
12408     },
12409    
12410 /**
12411  * Executes the specified function once for every item in the collection, passing each
12412  * item as the first and only parameter. returning false from the function will stop the iteration.
12413  * @param {Function} fn The function to execute for each item.
12414  * @param {Object} scope (optional) The scope in which to execute the function.
12415  */
12416     each : function(fn, scope){
12417         var items = [].concat(this.items); // each safe for removal
12418         for(var i = 0, len = items.length; i < len; i++){
12419             if(fn.call(scope || items[i], items[i], i, len) === false){
12420                 break;
12421             }
12422         }
12423     },
12424    
12425 /**
12426  * Executes the specified function once for every key in the collection, passing each
12427  * key, and its associated item as the first two parameters.
12428  * @param {Function} fn The function to execute for each item.
12429  * @param {Object} scope (optional) The scope in which to execute the function.
12430  */
12431     eachKey : function(fn, scope){
12432         for(var i = 0, len = this.keys.length; i < len; i++){
12433             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12434         }
12435     },
12436    
12437 /**
12438  * Returns the first item in the collection which elicits a true return value from the
12439  * passed selection function.
12440  * @param {Function} fn The selection function to execute for each item.
12441  * @param {Object} scope (optional) The scope in which to execute the function.
12442  * @return {Object} The first item in the collection which returned true from the selection function.
12443  */
12444     find : function(fn, scope){
12445         for(var i = 0, len = this.items.length; i < len; i++){
12446             if(fn.call(scope || window, this.items[i], this.keys[i])){
12447                 return this.items[i];
12448             }
12449         }
12450         return null;
12451     },
12452    
12453 /**
12454  * Inserts an item at the specified index in the collection.
12455  * @param {Number} index The index to insert the item at.
12456  * @param {String} key The key to associate with the new item, or the item itself.
12457  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12458  * @return {Object} The item inserted.
12459  */
12460     insert : function(index, key, o){
12461         if(arguments.length == 2){
12462             o = arguments[1];
12463             key = this.getKey(o);
12464         }
12465         if(index >= this.length){
12466             return this.add(key, o);
12467         }
12468         this.length++;
12469         this.items.splice(index, 0, o);
12470         if(typeof key != "undefined" && key != null){
12471             this.map[key] = o;
12472         }
12473         this.keys.splice(index, 0, key);
12474         this.fireEvent("add", index, o, key);
12475         return o;
12476     },
12477    
12478 /**
12479  * Removed an item from the collection.
12480  * @param {Object} o The item to remove.
12481  * @return {Object} The item removed.
12482  */
12483     remove : function(o){
12484         return this.removeAt(this.indexOf(o));
12485     },
12486    
12487 /**
12488  * Remove an item from a specified index in the collection.
12489  * @param {Number} index The index within the collection of the item to remove.
12490  */
12491     removeAt : function(index){
12492         if(index < this.length && index >= 0){
12493             this.length--;
12494             var o = this.items[index];
12495             this.items.splice(index, 1);
12496             var key = this.keys[index];
12497             if(typeof key != "undefined"){
12498                 delete this.map[key];
12499             }
12500             this.keys.splice(index, 1);
12501             this.fireEvent("remove", o, key);
12502         }
12503     },
12504    
12505 /**
12506  * Removed an item associated with the passed key fom the collection.
12507  * @param {String} key The key of the item to remove.
12508  */
12509     removeKey : function(key){
12510         return this.removeAt(this.indexOfKey(key));
12511     },
12512    
12513 /**
12514  * Returns the number of items in the collection.
12515  * @return {Number} the number of items in the collection.
12516  */
12517     getCount : function(){
12518         return this.length; 
12519     },
12520    
12521 /**
12522  * Returns index within the collection of the passed Object.
12523  * @param {Object} o The item to find the index of.
12524  * @return {Number} index of the item.
12525  */
12526     indexOf : function(o){
12527         if(!this.items.indexOf){
12528             for(var i = 0, len = this.items.length; i < len; i++){
12529                 if(this.items[i] == o) return i;
12530             }
12531             return -1;
12532         }else{
12533             return this.items.indexOf(o);
12534         }
12535     },
12536    
12537 /**
12538  * Returns index within the collection of the passed key.
12539  * @param {String} key The key to find the index of.
12540  * @return {Number} index of the key.
12541  */
12542     indexOfKey : function(key){
12543         if(!this.keys.indexOf){
12544             for(var i = 0, len = this.keys.length; i < len; i++){
12545                 if(this.keys[i] == key) return i;
12546             }
12547             return -1;
12548         }else{
12549             return this.keys.indexOf(key);
12550         }
12551     },
12552    
12553 /**
12554  * Returns the item associated with the passed key OR index. Key has priority over index.
12555  * @param {String/Number} key The key or index of the item.
12556  * @return {Object} The item associated with the passed key.
12557  */
12558     item : function(key){
12559         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12560         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12561     },
12562     
12563 /**
12564  * Returns the item at the specified index.
12565  * @param {Number} index The index of the item.
12566  * @return {Object}
12567  */
12568     itemAt : function(index){
12569         return this.items[index];
12570     },
12571     
12572 /**
12573  * Returns the item associated with the passed key.
12574  * @param {String/Number} key The key of the item.
12575  * @return {Object} The item associated with the passed key.
12576  */
12577     key : function(key){
12578         return this.map[key];
12579     },
12580    
12581 /**
12582  * Returns true if the collection contains the passed Object as an item.
12583  * @param {Object} o  The Object to look for in the collection.
12584  * @return {Boolean} True if the collection contains the Object as an item.
12585  */
12586     contains : function(o){
12587         return this.indexOf(o) != -1;
12588     },
12589    
12590 /**
12591  * Returns true if the collection contains the passed Object as a key.
12592  * @param {String} key The key to look for in the collection.
12593  * @return {Boolean} True if the collection contains the Object as a key.
12594  */
12595     containsKey : function(key){
12596         return typeof this.map[key] != "undefined";
12597     },
12598    
12599 /**
12600  * Removes all items from the collection.
12601  */
12602     clear : function(){
12603         this.length = 0;
12604         this.items = [];
12605         this.keys = [];
12606         this.map = {};
12607         this.fireEvent("clear");
12608     },
12609    
12610 /**
12611  * Returns the first item in the collection.
12612  * @return {Object} the first item in the collection..
12613  */
12614     first : function(){
12615         return this.items[0]; 
12616     },
12617    
12618 /**
12619  * Returns the last item in the collection.
12620  * @return {Object} the last item in the collection..
12621  */
12622     last : function(){
12623         return this.items[this.length-1];   
12624     },
12625     
12626     _sort : function(property, dir, fn){
12627         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12628         fn = fn || function(a, b){
12629             return a-b;
12630         };
12631         var c = [], k = this.keys, items = this.items;
12632         for(var i = 0, len = items.length; i < len; i++){
12633             c[c.length] = {key: k[i], value: items[i], index: i};
12634         }
12635         c.sort(function(a, b){
12636             var v = fn(a[property], b[property]) * dsc;
12637             if(v == 0){
12638                 v = (a.index < b.index ? -1 : 1);
12639             }
12640             return v;
12641         });
12642         for(var i = 0, len = c.length; i < len; i++){
12643             items[i] = c[i].value;
12644             k[i] = c[i].key;
12645         }
12646         this.fireEvent("sort", this);
12647     },
12648     
12649     /**
12650      * Sorts this collection with the passed comparison function
12651      * @param {String} direction (optional) "ASC" or "DESC"
12652      * @param {Function} fn (optional) comparison function
12653      */
12654     sort : function(dir, fn){
12655         this._sort("value", dir, fn);
12656     },
12657     
12658     /**
12659      * Sorts this collection by keys
12660      * @param {String} direction (optional) "ASC" or "DESC"
12661      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12662      */
12663     keySort : function(dir, fn){
12664         this._sort("key", dir, fn || function(a, b){
12665             return String(a).toUpperCase()-String(b).toUpperCase();
12666         });
12667     },
12668     
12669     /**
12670      * Returns a range of items in this collection
12671      * @param {Number} startIndex (optional) defaults to 0
12672      * @param {Number} endIndex (optional) default to the last item
12673      * @return {Array} An array of items
12674      */
12675     getRange : function(start, end){
12676         var items = this.items;
12677         if(items.length < 1){
12678             return [];
12679         }
12680         start = start || 0;
12681         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12682         var r = [];
12683         if(start <= end){
12684             for(var i = start; i <= end; i++) {
12685                     r[r.length] = items[i];
12686             }
12687         }else{
12688             for(var i = start; i >= end; i--) {
12689                     r[r.length] = items[i];
12690             }
12691         }
12692         return r;
12693     },
12694         
12695     /**
12696      * Filter the <i>objects</i> in this collection by a specific property. 
12697      * Returns a new collection that has been filtered.
12698      * @param {String} property A property on your objects
12699      * @param {String/RegExp} value Either string that the property values 
12700      * should start with or a RegExp to test against the property
12701      * @return {MixedCollection} The new filtered collection
12702      */
12703     filter : function(property, value){
12704         if(!value.exec){ // not a regex
12705             value = String(value);
12706             if(value.length == 0){
12707                 return this.clone();
12708             }
12709             value = new RegExp("^" + Roo.escapeRe(value), "i");
12710         }
12711         return this.filterBy(function(o){
12712             return o && value.test(o[property]);
12713         });
12714         },
12715     
12716     /**
12717      * Filter by a function. * Returns a new collection that has been filtered.
12718      * The passed function will be called with each 
12719      * object in the collection. If the function returns true, the value is included 
12720      * otherwise it is filtered.
12721      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12722      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12723      * @return {MixedCollection} The new filtered collection
12724      */
12725     filterBy : function(fn, scope){
12726         var r = new Roo.util.MixedCollection();
12727         r.getKey = this.getKey;
12728         var k = this.keys, it = this.items;
12729         for(var i = 0, len = it.length; i < len; i++){
12730             if(fn.call(scope||this, it[i], k[i])){
12731                                 r.add(k[i], it[i]);
12732                         }
12733         }
12734         return r;
12735     },
12736     
12737     /**
12738      * Creates a duplicate of this collection
12739      * @return {MixedCollection}
12740      */
12741     clone : function(){
12742         var r = new Roo.util.MixedCollection();
12743         var k = this.keys, it = this.items;
12744         for(var i = 0, len = it.length; i < len; i++){
12745             r.add(k[i], it[i]);
12746         }
12747         r.getKey = this.getKey;
12748         return r;
12749     }
12750 });
12751 /**
12752  * Returns the item associated with the passed key or index.
12753  * @method
12754  * @param {String/Number} key The key or index of the item.
12755  * @return {Object} The item associated with the passed key.
12756  */
12757 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12758  * Based on:
12759  * Ext JS Library 1.1.1
12760  * Copyright(c) 2006-2007, Ext JS, LLC.
12761  *
12762  * Originally Released Under LGPL - original licence link has changed is not relivant.
12763  *
12764  * Fork - LGPL
12765  * <script type="text/javascript">
12766  */
12767 /**
12768  * @class Roo.util.JSON
12769  * Modified version of Douglas Crockford"s json.js that doesn"t
12770  * mess with the Object prototype 
12771  * http://www.json.org/js.html
12772  * @singleton
12773  */
12774 Roo.util.JSON = new (function(){
12775     var useHasOwn = {}.hasOwnProperty ? true : false;
12776     
12777     // crashes Safari in some instances
12778     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12779     
12780     var pad = function(n) {
12781         return n < 10 ? "0" + n : n;
12782     };
12783     
12784     var m = {
12785         "\b": '\\b',
12786         "\t": '\\t',
12787         "\n": '\\n',
12788         "\f": '\\f',
12789         "\r": '\\r',
12790         '"' : '\\"',
12791         "\\": '\\\\'
12792     };
12793
12794     var encodeString = function(s){
12795         if (/["\\\x00-\x1f]/.test(s)) {
12796             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12797                 var c = m[b];
12798                 if(c){
12799                     return c;
12800                 }
12801                 c = b.charCodeAt();
12802                 return "\\u00" +
12803                     Math.floor(c / 16).toString(16) +
12804                     (c % 16).toString(16);
12805             }) + '"';
12806         }
12807         return '"' + s + '"';
12808     };
12809     
12810     var encodeArray = function(o){
12811         var a = ["["], b, i, l = o.length, v;
12812             for (i = 0; i < l; i += 1) {
12813                 v = o[i];
12814                 switch (typeof v) {
12815                     case "undefined":
12816                     case "function":
12817                     case "unknown":
12818                         break;
12819                     default:
12820                         if (b) {
12821                             a.push(',');
12822                         }
12823                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12824                         b = true;
12825                 }
12826             }
12827             a.push("]");
12828             return a.join("");
12829     };
12830     
12831     var encodeDate = function(o){
12832         return '"' + o.getFullYear() + "-" +
12833                 pad(o.getMonth() + 1) + "-" +
12834                 pad(o.getDate()) + "T" +
12835                 pad(o.getHours()) + ":" +
12836                 pad(o.getMinutes()) + ":" +
12837                 pad(o.getSeconds()) + '"';
12838     };
12839     
12840     /**
12841      * Encodes an Object, Array or other value
12842      * @param {Mixed} o The variable to encode
12843      * @return {String} The JSON string
12844      */
12845     this.encode = function(o)
12846     {
12847         // should this be extended to fully wrap stringify..
12848         
12849         if(typeof o == "undefined" || o === null){
12850             return "null";
12851         }else if(o instanceof Array){
12852             return encodeArray(o);
12853         }else if(o instanceof Date){
12854             return encodeDate(o);
12855         }else if(typeof o == "string"){
12856             return encodeString(o);
12857         }else if(typeof o == "number"){
12858             return isFinite(o) ? String(o) : "null";
12859         }else if(typeof o == "boolean"){
12860             return String(o);
12861         }else {
12862             var a = ["{"], b, i, v;
12863             for (i in o) {
12864                 if(!useHasOwn || o.hasOwnProperty(i)) {
12865                     v = o[i];
12866                     switch (typeof v) {
12867                     case "undefined":
12868                     case "function":
12869                     case "unknown":
12870                         break;
12871                     default:
12872                         if(b){
12873                             a.push(',');
12874                         }
12875                         a.push(this.encode(i), ":",
12876                                 v === null ? "null" : this.encode(v));
12877                         b = true;
12878                     }
12879                 }
12880             }
12881             a.push("}");
12882             return a.join("");
12883         }
12884     };
12885     
12886     /**
12887      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12888      * @param {String} json The JSON string
12889      * @return {Object} The resulting object
12890      */
12891     this.decode = function(json){
12892         
12893         return  /** eval:var:json */ eval("(" + json + ')');
12894     };
12895 })();
12896 /** 
12897  * Shorthand for {@link Roo.util.JSON#encode}
12898  * @member Roo encode 
12899  * @method */
12900 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12901 /** 
12902  * Shorthand for {@link Roo.util.JSON#decode}
12903  * @member Roo decode 
12904  * @method */
12905 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12906 /*
12907  * Based on:
12908  * Ext JS Library 1.1.1
12909  * Copyright(c) 2006-2007, Ext JS, LLC.
12910  *
12911  * Originally Released Under LGPL - original licence link has changed is not relivant.
12912  *
12913  * Fork - LGPL
12914  * <script type="text/javascript">
12915  */
12916  
12917 /**
12918  * @class Roo.util.Format
12919  * Reusable data formatting functions
12920  * @singleton
12921  */
12922 Roo.util.Format = function(){
12923     var trimRe = /^\s+|\s+$/g;
12924     return {
12925         /**
12926          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12927          * @param {String} value The string to truncate
12928          * @param {Number} length The maximum length to allow before truncating
12929          * @return {String} The converted text
12930          */
12931         ellipsis : function(value, len){
12932             if(value && value.length > len){
12933                 return value.substr(0, len-3)+"...";
12934             }
12935             return value;
12936         },
12937
12938         /**
12939          * Checks a reference and converts it to empty string if it is undefined
12940          * @param {Mixed} value Reference to check
12941          * @return {Mixed} Empty string if converted, otherwise the original value
12942          */
12943         undef : function(value){
12944             return typeof value != "undefined" ? value : "";
12945         },
12946
12947         /**
12948          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12949          * @param {String} value The string to encode
12950          * @return {String} The encoded text
12951          */
12952         htmlEncode : function(value){
12953             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12954         },
12955
12956         /**
12957          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12958          * @param {String} value The string to decode
12959          * @return {String} The decoded text
12960          */
12961         htmlDecode : function(value){
12962             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12963         },
12964
12965         /**
12966          * Trims any whitespace from either side of a string
12967          * @param {String} value The text to trim
12968          * @return {String} The trimmed text
12969          */
12970         trim : function(value){
12971             return String(value).replace(trimRe, "");
12972         },
12973
12974         /**
12975          * Returns a substring from within an original string
12976          * @param {String} value The original text
12977          * @param {Number} start The start index of the substring
12978          * @param {Number} length The length of the substring
12979          * @return {String} The substring
12980          */
12981         substr : function(value, start, length){
12982             return String(value).substr(start, length);
12983         },
12984
12985         /**
12986          * Converts a string to all lower case letters
12987          * @param {String} value The text to convert
12988          * @return {String} The converted text
12989          */
12990         lowercase : function(value){
12991             return String(value).toLowerCase();
12992         },
12993
12994         /**
12995          * Converts a string to all upper case letters
12996          * @param {String} value The text to convert
12997          * @return {String} The converted text
12998          */
12999         uppercase : function(value){
13000             return String(value).toUpperCase();
13001         },
13002
13003         /**
13004          * Converts the first character only of a string to upper case
13005          * @param {String} value The text to convert
13006          * @return {String} The converted text
13007          */
13008         capitalize : function(value){
13009             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13010         },
13011
13012         // private
13013         call : function(value, fn){
13014             if(arguments.length > 2){
13015                 var args = Array.prototype.slice.call(arguments, 2);
13016                 args.unshift(value);
13017                  
13018                 return /** eval:var:value */  eval(fn).apply(window, args);
13019             }else{
13020                 /** eval:var:value */
13021                 return /** eval:var:value */ eval(fn).call(window, value);
13022             }
13023         },
13024
13025        
13026         /**
13027          * safer version of Math.toFixed..??/
13028          * @param {Number/String} value The numeric value to format
13029          * @param {Number/String} value Decimal places 
13030          * @return {String} The formatted currency string
13031          */
13032         toFixed : function(v, n)
13033         {
13034             // why not use to fixed - precision is buggered???
13035             if (!n) {
13036                 return Math.round(v-0);
13037             }
13038             var fact = Math.pow(10,n+1);
13039             v = (Math.round((v-0)*fact))/fact;
13040             var z = (''+fact).substring(2);
13041             if (v == Math.floor(v)) {
13042                 return Math.floor(v) + '.' + z;
13043             }
13044             
13045             // now just padd decimals..
13046             var ps = String(v).split('.');
13047             var fd = (ps[1] + z);
13048             var r = fd.substring(0,n); 
13049             var rm = fd.substring(n); 
13050             if (rm < 5) {
13051                 return ps[0] + '.' + r;
13052             }
13053             r*=1; // turn it into a number;
13054             r++;
13055             if (String(r).length != n) {
13056                 ps[0]*=1;
13057                 ps[0]++;
13058                 r = String(r).substring(1); // chop the end off.
13059             }
13060             
13061             return ps[0] + '.' + r;
13062              
13063         },
13064         
13065         /**
13066          * Format a number as US currency
13067          * @param {Number/String} value The numeric value to format
13068          * @return {String} The formatted currency string
13069          */
13070         usMoney : function(v){
13071             v = (Math.round((v-0)*100))/100;
13072             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13073             v = String(v);
13074             var ps = v.split('.');
13075             var whole = ps[0];
13076             var sub = ps[1] ? '.'+ ps[1] : '.00';
13077             var r = /(\d+)(\d{3})/;
13078             while (r.test(whole)) {
13079                 whole = whole.replace(r, '$1' + ',' + '$2');
13080             }
13081             return "$" + whole + sub ;
13082         },
13083         
13084         /**
13085          * Parse a value into a formatted date using the specified format pattern.
13086          * @param {Mixed} value The value to format
13087          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13088          * @return {String} The formatted date string
13089          */
13090         date : function(v, format){
13091             if(!v){
13092                 return "";
13093             }
13094             if(!(v instanceof Date)){
13095                 v = new Date(Date.parse(v));
13096             }
13097             return v.dateFormat(format || "m/d/Y");
13098         },
13099
13100         /**
13101          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13102          * @param {String} format Any valid date format string
13103          * @return {Function} The date formatting function
13104          */
13105         dateRenderer : function(format){
13106             return function(v){
13107                 return Roo.util.Format.date(v, format);  
13108             };
13109         },
13110
13111         // private
13112         stripTagsRE : /<\/?[^>]+>/gi,
13113         
13114         /**
13115          * Strips all HTML tags
13116          * @param {Mixed} value The text from which to strip tags
13117          * @return {String} The stripped text
13118          */
13119         stripTags : function(v){
13120             return !v ? v : String(v).replace(this.stripTagsRE, "");
13121         }
13122     };
13123 }();/*
13124  * Based on:
13125  * Ext JS Library 1.1.1
13126  * Copyright(c) 2006-2007, Ext JS, LLC.
13127  *
13128  * Originally Released Under LGPL - original licence link has changed is not relivant.
13129  *
13130  * Fork - LGPL
13131  * <script type="text/javascript">
13132  */
13133
13134
13135  
13136
13137 /**
13138  * @class Roo.MasterTemplate
13139  * @extends Roo.Template
13140  * Provides a template that can have child templates. The syntax is:
13141 <pre><code>
13142 var t = new Roo.MasterTemplate(
13143         '&lt;select name="{name}"&gt;',
13144                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13145         '&lt;/select&gt;'
13146 );
13147 t.add('options', {value: 'foo', text: 'bar'});
13148 // or you can add multiple child elements in one shot
13149 t.addAll('options', [
13150     {value: 'foo', text: 'bar'},
13151     {value: 'foo2', text: 'bar2'},
13152     {value: 'foo3', text: 'bar3'}
13153 ]);
13154 // then append, applying the master template values
13155 t.append('my-form', {name: 'my-select'});
13156 </code></pre>
13157 * A name attribute for the child template is not required if you have only one child
13158 * template or you want to refer to them by index.
13159  */
13160 Roo.MasterTemplate = function(){
13161     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13162     this.originalHtml = this.html;
13163     var st = {};
13164     var m, re = this.subTemplateRe;
13165     re.lastIndex = 0;
13166     var subIndex = 0;
13167     while(m = re.exec(this.html)){
13168         var name = m[1], content = m[2];
13169         st[subIndex] = {
13170             name: name,
13171             index: subIndex,
13172             buffer: [],
13173             tpl : new Roo.Template(content)
13174         };
13175         if(name){
13176             st[name] = st[subIndex];
13177         }
13178         st[subIndex].tpl.compile();
13179         st[subIndex].tpl.call = this.call.createDelegate(this);
13180         subIndex++;
13181     }
13182     this.subCount = subIndex;
13183     this.subs = st;
13184 };
13185 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13186     /**
13187     * The regular expression used to match sub templates
13188     * @type RegExp
13189     * @property
13190     */
13191     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13192
13193     /**
13194      * Applies the passed values to a child template.
13195      * @param {String/Number} name (optional) The name or index of the child template
13196      * @param {Array/Object} values The values to be applied to the template
13197      * @return {MasterTemplate} this
13198      */
13199      add : function(name, values){
13200         if(arguments.length == 1){
13201             values = arguments[0];
13202             name = 0;
13203         }
13204         var s = this.subs[name];
13205         s.buffer[s.buffer.length] = s.tpl.apply(values);
13206         return this;
13207     },
13208
13209     /**
13210      * Applies all the passed values to a child template.
13211      * @param {String/Number} name (optional) The name or index of the child template
13212      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13213      * @param {Boolean} reset (optional) True to reset the template first
13214      * @return {MasterTemplate} this
13215      */
13216     fill : function(name, values, reset){
13217         var a = arguments;
13218         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13219             values = a[0];
13220             name = 0;
13221             reset = a[1];
13222         }
13223         if(reset){
13224             this.reset();
13225         }
13226         for(var i = 0, len = values.length; i < len; i++){
13227             this.add(name, values[i]);
13228         }
13229         return this;
13230     },
13231
13232     /**
13233      * Resets the template for reuse
13234      * @return {MasterTemplate} this
13235      */
13236      reset : function(){
13237         var s = this.subs;
13238         for(var i = 0; i < this.subCount; i++){
13239             s[i].buffer = [];
13240         }
13241         return this;
13242     },
13243
13244     applyTemplate : function(values){
13245         var s = this.subs;
13246         var replaceIndex = -1;
13247         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13248             return s[++replaceIndex].buffer.join("");
13249         });
13250         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13251     },
13252
13253     apply : function(){
13254         return this.applyTemplate.apply(this, arguments);
13255     },
13256
13257     compile : function(){return this;}
13258 });
13259
13260 /**
13261  * Alias for fill().
13262  * @method
13263  */
13264 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13265  /**
13266  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13267  * var tpl = Roo.MasterTemplate.from('element-id');
13268  * @param {String/HTMLElement} el
13269  * @param {Object} config
13270  * @static
13271  */
13272 Roo.MasterTemplate.from = function(el, config){
13273     el = Roo.getDom(el);
13274     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13275 };/*
13276  * Based on:
13277  * Ext JS Library 1.1.1
13278  * Copyright(c) 2006-2007, Ext JS, LLC.
13279  *
13280  * Originally Released Under LGPL - original licence link has changed is not relivant.
13281  *
13282  * Fork - LGPL
13283  * <script type="text/javascript">
13284  */
13285
13286  
13287 /**
13288  * @class Roo.util.CSS
13289  * Utility class for manipulating CSS rules
13290  * @singleton
13291  */
13292 Roo.util.CSS = function(){
13293         var rules = null;
13294         var doc = document;
13295
13296     var camelRe = /(-[a-z])/gi;
13297     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13298
13299    return {
13300    /**
13301     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13302     * tag and appended to the HEAD of the document.
13303     * @param {String|Object} cssText The text containing the css rules
13304     * @param {String} id An id to add to the stylesheet for later removal
13305     * @return {StyleSheet}
13306     */
13307     createStyleSheet : function(cssText, id){
13308         var ss;
13309         var head = doc.getElementsByTagName("head")[0];
13310         var nrules = doc.createElement("style");
13311         nrules.setAttribute("type", "text/css");
13312         if(id){
13313             nrules.setAttribute("id", id);
13314         }
13315         if (typeof(cssText) != 'string') {
13316             // support object maps..
13317             // not sure if this a good idea.. 
13318             // perhaps it should be merged with the general css handling
13319             // and handle js style props.
13320             var cssTextNew = [];
13321             for(var n in cssText) {
13322                 var citems = [];
13323                 for(var k in cssText[n]) {
13324                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13325                 }
13326                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13327                 
13328             }
13329             cssText = cssTextNew.join("\n");
13330             
13331         }
13332        
13333        
13334        if(Roo.isIE){
13335            head.appendChild(nrules);
13336            ss = nrules.styleSheet;
13337            ss.cssText = cssText;
13338        }else{
13339            try{
13340                 nrules.appendChild(doc.createTextNode(cssText));
13341            }catch(e){
13342                nrules.cssText = cssText; 
13343            }
13344            head.appendChild(nrules);
13345            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13346        }
13347        this.cacheStyleSheet(ss);
13348        return ss;
13349    },
13350
13351    /**
13352     * Removes a style or link tag by id
13353     * @param {String} id The id of the tag
13354     */
13355    removeStyleSheet : function(id){
13356        var existing = doc.getElementById(id);
13357        if(existing){
13358            existing.parentNode.removeChild(existing);
13359        }
13360    },
13361
13362    /**
13363     * Dynamically swaps an existing stylesheet reference for a new one
13364     * @param {String} id The id of an existing link tag to remove
13365     * @param {String} url The href of the new stylesheet to include
13366     */
13367    swapStyleSheet : function(id, url){
13368        this.removeStyleSheet(id);
13369        var ss = doc.createElement("link");
13370        ss.setAttribute("rel", "stylesheet");
13371        ss.setAttribute("type", "text/css");
13372        ss.setAttribute("id", id);
13373        ss.setAttribute("href", url);
13374        doc.getElementsByTagName("head")[0].appendChild(ss);
13375    },
13376    
13377    /**
13378     * Refresh the rule cache if you have dynamically added stylesheets
13379     * @return {Object} An object (hash) of rules indexed by selector
13380     */
13381    refreshCache : function(){
13382        return this.getRules(true);
13383    },
13384
13385    // private
13386    cacheStyleSheet : function(stylesheet){
13387        if(!rules){
13388            rules = {};
13389        }
13390        try{// try catch for cross domain access issue
13391            var ssRules = stylesheet.cssRules || stylesheet.rules;
13392            for(var j = ssRules.length-1; j >= 0; --j){
13393                rules[ssRules[j].selectorText] = ssRules[j];
13394            }
13395        }catch(e){}
13396    },
13397    
13398    /**
13399     * Gets all css rules for the document
13400     * @param {Boolean} refreshCache true to refresh the internal cache
13401     * @return {Object} An object (hash) of rules indexed by selector
13402     */
13403    getRules : function(refreshCache){
13404                 if(rules == null || refreshCache){
13405                         rules = {};
13406                         var ds = doc.styleSheets;
13407                         for(var i =0, len = ds.length; i < len; i++){
13408                             try{
13409                         this.cacheStyleSheet(ds[i]);
13410                     }catch(e){} 
13411                 }
13412                 }
13413                 return rules;
13414         },
13415         
13416         /**
13417     * Gets an an individual CSS rule by selector(s)
13418     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13419     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13420     * @return {CSSRule} The CSS rule or null if one is not found
13421     */
13422    getRule : function(selector, refreshCache){
13423                 var rs = this.getRules(refreshCache);
13424                 if(!(selector instanceof Array)){
13425                     return rs[selector];
13426                 }
13427                 for(var i = 0; i < selector.length; i++){
13428                         if(rs[selector[i]]){
13429                                 return rs[selector[i]];
13430                         }
13431                 }
13432                 return null;
13433         },
13434         
13435         
13436         /**
13437     * Updates a rule property
13438     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13439     * @param {String} property The css property
13440     * @param {String} value The new value for the property
13441     * @return {Boolean} true If a rule was found and updated
13442     */
13443    updateRule : function(selector, property, value){
13444                 if(!(selector instanceof Array)){
13445                         var rule = this.getRule(selector);
13446                         if(rule){
13447                                 rule.style[property.replace(camelRe, camelFn)] = value;
13448                                 return true;
13449                         }
13450                 }else{
13451                         for(var i = 0; i < selector.length; i++){
13452                                 if(this.updateRule(selector[i], property, value)){
13453                                         return true;
13454                                 }
13455                         }
13456                 }
13457                 return false;
13458         }
13459    };   
13460 }();/*
13461  * Based on:
13462  * Ext JS Library 1.1.1
13463  * Copyright(c) 2006-2007, Ext JS, LLC.
13464  *
13465  * Originally Released Under LGPL - original licence link has changed is not relivant.
13466  *
13467  * Fork - LGPL
13468  * <script type="text/javascript">
13469  */
13470
13471  
13472
13473 /**
13474  * @class Roo.util.ClickRepeater
13475  * @extends Roo.util.Observable
13476  * 
13477  * A wrapper class which can be applied to any element. Fires a "click" event while the
13478  * mouse is pressed. The interval between firings may be specified in the config but
13479  * defaults to 10 milliseconds.
13480  * 
13481  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13482  * 
13483  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13484  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13485  * Similar to an autorepeat key delay.
13486  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13487  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13488  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13489  *           "interval" and "delay" are ignored. "immediate" is honored.
13490  * @cfg {Boolean} preventDefault True to prevent the default click event
13491  * @cfg {Boolean} stopDefault True to stop the default click event
13492  * 
13493  * @history
13494  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13495  *     2007-02-02 jvs Renamed to ClickRepeater
13496  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13497  *
13498  *  @constructor
13499  * @param {String/HTMLElement/Element} el The element to listen on
13500  * @param {Object} config
13501  **/
13502 Roo.util.ClickRepeater = function(el, config)
13503 {
13504     this.el = Roo.get(el);
13505     this.el.unselectable();
13506
13507     Roo.apply(this, config);
13508
13509     this.addEvents({
13510     /**
13511      * @event mousedown
13512      * Fires when the mouse button is depressed.
13513      * @param {Roo.util.ClickRepeater} this
13514      */
13515         "mousedown" : true,
13516     /**
13517      * @event click
13518      * Fires on a specified interval during the time the element is pressed.
13519      * @param {Roo.util.ClickRepeater} this
13520      */
13521         "click" : true,
13522     /**
13523      * @event mouseup
13524      * Fires when the mouse key is released.
13525      * @param {Roo.util.ClickRepeater} this
13526      */
13527         "mouseup" : true
13528     });
13529
13530     this.el.on("mousedown", this.handleMouseDown, this);
13531     if(this.preventDefault || this.stopDefault){
13532         this.el.on("click", function(e){
13533             if(this.preventDefault){
13534                 e.preventDefault();
13535             }
13536             if(this.stopDefault){
13537                 e.stopEvent();
13538             }
13539         }, this);
13540     }
13541
13542     // allow inline handler
13543     if(this.handler){
13544         this.on("click", this.handler,  this.scope || this);
13545     }
13546
13547     Roo.util.ClickRepeater.superclass.constructor.call(this);
13548 };
13549
13550 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13551     interval : 20,
13552     delay: 250,
13553     preventDefault : true,
13554     stopDefault : false,
13555     timer : 0,
13556
13557     // private
13558     handleMouseDown : function(){
13559         clearTimeout(this.timer);
13560         this.el.blur();
13561         if(this.pressClass){
13562             this.el.addClass(this.pressClass);
13563         }
13564         this.mousedownTime = new Date();
13565
13566         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13567         this.el.on("mouseout", this.handleMouseOut, this);
13568
13569         this.fireEvent("mousedown", this);
13570         this.fireEvent("click", this);
13571         
13572         this.timer = this.click.defer(this.delay || this.interval, this);
13573     },
13574
13575     // private
13576     click : function(){
13577         this.fireEvent("click", this);
13578         this.timer = this.click.defer(this.getInterval(), this);
13579     },
13580
13581     // private
13582     getInterval: function(){
13583         if(!this.accelerate){
13584             return this.interval;
13585         }
13586         var pressTime = this.mousedownTime.getElapsed();
13587         if(pressTime < 500){
13588             return 400;
13589         }else if(pressTime < 1700){
13590             return 320;
13591         }else if(pressTime < 2600){
13592             return 250;
13593         }else if(pressTime < 3500){
13594             return 180;
13595         }else if(pressTime < 4400){
13596             return 140;
13597         }else if(pressTime < 5300){
13598             return 80;
13599         }else if(pressTime < 6200){
13600             return 50;
13601         }else{
13602             return 10;
13603         }
13604     },
13605
13606     // private
13607     handleMouseOut : function(){
13608         clearTimeout(this.timer);
13609         if(this.pressClass){
13610             this.el.removeClass(this.pressClass);
13611         }
13612         this.el.on("mouseover", this.handleMouseReturn, this);
13613     },
13614
13615     // private
13616     handleMouseReturn : function(){
13617         this.el.un("mouseover", this.handleMouseReturn);
13618         if(this.pressClass){
13619             this.el.addClass(this.pressClass);
13620         }
13621         this.click();
13622     },
13623
13624     // private
13625     handleMouseUp : function(){
13626         clearTimeout(this.timer);
13627         this.el.un("mouseover", this.handleMouseReturn);
13628         this.el.un("mouseout", this.handleMouseOut);
13629         Roo.get(document).un("mouseup", this.handleMouseUp);
13630         this.el.removeClass(this.pressClass);
13631         this.fireEvent("mouseup", this);
13632     }
13633 });/*
13634  * Based on:
13635  * Ext JS Library 1.1.1
13636  * Copyright(c) 2006-2007, Ext JS, LLC.
13637  *
13638  * Originally Released Under LGPL - original licence link has changed is not relivant.
13639  *
13640  * Fork - LGPL
13641  * <script type="text/javascript">
13642  */
13643
13644  
13645 /**
13646  * @class Roo.KeyNav
13647  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13648  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13649  * way to implement custom navigation schemes for any UI component.</p>
13650  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13651  * pageUp, pageDown, del, home, end.  Usage:</p>
13652  <pre><code>
13653 var nav = new Roo.KeyNav("my-element", {
13654     "left" : function(e){
13655         this.moveLeft(e.ctrlKey);
13656     },
13657     "right" : function(e){
13658         this.moveRight(e.ctrlKey);
13659     },
13660     "enter" : function(e){
13661         this.save();
13662     },
13663     scope : this
13664 });
13665 </code></pre>
13666  * @constructor
13667  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13668  * @param {Object} config The config
13669  */
13670 Roo.KeyNav = function(el, config){
13671     this.el = Roo.get(el);
13672     Roo.apply(this, config);
13673     if(!this.disabled){
13674         this.disabled = true;
13675         this.enable();
13676     }
13677 };
13678
13679 Roo.KeyNav.prototype = {
13680     /**
13681      * @cfg {Boolean} disabled
13682      * True to disable this KeyNav instance (defaults to false)
13683      */
13684     disabled : false,
13685     /**
13686      * @cfg {String} defaultEventAction
13687      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13688      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13689      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13690      */
13691     defaultEventAction: "stopEvent",
13692     /**
13693      * @cfg {Boolean} forceKeyDown
13694      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13695      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13696      * handle keydown instead of keypress.
13697      */
13698     forceKeyDown : false,
13699
13700     // private
13701     prepareEvent : function(e){
13702         var k = e.getKey();
13703         var h = this.keyToHandler[k];
13704         //if(h && this[h]){
13705         //    e.stopPropagation();
13706         //}
13707         if(Roo.isSafari && h && k >= 37 && k <= 40){
13708             e.stopEvent();
13709         }
13710     },
13711
13712     // private
13713     relay : function(e){
13714         var k = e.getKey();
13715         var h = this.keyToHandler[k];
13716         if(h && this[h]){
13717             if(this.doRelay(e, this[h], h) !== true){
13718                 e[this.defaultEventAction]();
13719             }
13720         }
13721     },
13722
13723     // private
13724     doRelay : function(e, h, hname){
13725         return h.call(this.scope || this, e);
13726     },
13727
13728     // possible handlers
13729     enter : false,
13730     left : false,
13731     right : false,
13732     up : false,
13733     down : false,
13734     tab : false,
13735     esc : false,
13736     pageUp : false,
13737     pageDown : false,
13738     del : false,
13739     home : false,
13740     end : false,
13741
13742     // quick lookup hash
13743     keyToHandler : {
13744         37 : "left",
13745         39 : "right",
13746         38 : "up",
13747         40 : "down",
13748         33 : "pageUp",
13749         34 : "pageDown",
13750         46 : "del",
13751         36 : "home",
13752         35 : "end",
13753         13 : "enter",
13754         27 : "esc",
13755         9  : "tab"
13756     },
13757
13758         /**
13759          * Enable this KeyNav
13760          */
13761         enable: function(){
13762                 if(this.disabled){
13763             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13764             // the EventObject will normalize Safari automatically
13765             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13766                 this.el.on("keydown", this.relay,  this);
13767             }else{
13768                 this.el.on("keydown", this.prepareEvent,  this);
13769                 this.el.on("keypress", this.relay,  this);
13770             }
13771                     this.disabled = false;
13772                 }
13773         },
13774
13775         /**
13776          * Disable this KeyNav
13777          */
13778         disable: function(){
13779                 if(!this.disabled){
13780                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13781                 this.el.un("keydown", this.relay);
13782             }else{
13783                 this.el.un("keydown", this.prepareEvent);
13784                 this.el.un("keypress", this.relay);
13785             }
13786                     this.disabled = true;
13787                 }
13788         }
13789 };/*
13790  * Based on:
13791  * Ext JS Library 1.1.1
13792  * Copyright(c) 2006-2007, Ext JS, LLC.
13793  *
13794  * Originally Released Under LGPL - original licence link has changed is not relivant.
13795  *
13796  * Fork - LGPL
13797  * <script type="text/javascript">
13798  */
13799
13800  
13801 /**
13802  * @class Roo.KeyMap
13803  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13804  * The constructor accepts the same config object as defined by {@link #addBinding}.
13805  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13806  * combination it will call the function with this signature (if the match is a multi-key
13807  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13808  * A KeyMap can also handle a string representation of keys.<br />
13809  * Usage:
13810  <pre><code>
13811 // map one key by key code
13812 var map = new Roo.KeyMap("my-element", {
13813     key: 13, // or Roo.EventObject.ENTER
13814     fn: myHandler,
13815     scope: myObject
13816 });
13817
13818 // map multiple keys to one action by string
13819 var map = new Roo.KeyMap("my-element", {
13820     key: "a\r\n\t",
13821     fn: myHandler,
13822     scope: myObject
13823 });
13824
13825 // map multiple keys to multiple actions by strings and array of codes
13826 var map = new Roo.KeyMap("my-element", [
13827     {
13828         key: [10,13],
13829         fn: function(){ alert("Return was pressed"); }
13830     }, {
13831         key: "abc",
13832         fn: function(){ alert('a, b or c was pressed'); }
13833     }, {
13834         key: "\t",
13835         ctrl:true,
13836         shift:true,
13837         fn: function(){ alert('Control + shift + tab was pressed.'); }
13838     }
13839 ]);
13840 </code></pre>
13841  * <b>Note: A KeyMap starts enabled</b>
13842  * @constructor
13843  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13844  * @param {Object} config The config (see {@link #addBinding})
13845  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13846  */
13847 Roo.KeyMap = function(el, config, eventName){
13848     this.el  = Roo.get(el);
13849     this.eventName = eventName || "keydown";
13850     this.bindings = [];
13851     if(config){
13852         this.addBinding(config);
13853     }
13854     this.enable();
13855 };
13856
13857 Roo.KeyMap.prototype = {
13858     /**
13859      * True to stop the event from bubbling and prevent the default browser action if the
13860      * key was handled by the KeyMap (defaults to false)
13861      * @type Boolean
13862      */
13863     stopEvent : false,
13864
13865     /**
13866      * Add a new binding to this KeyMap. The following config object properties are supported:
13867      * <pre>
13868 Property    Type             Description
13869 ----------  ---------------  ----------------------------------------------------------------------
13870 key         String/Array     A single keycode or an array of keycodes to handle
13871 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13872 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13873 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13874 fn          Function         The function to call when KeyMap finds the expected key combination
13875 scope       Object           The scope of the callback function
13876 </pre>
13877      *
13878      * Usage:
13879      * <pre><code>
13880 // Create a KeyMap
13881 var map = new Roo.KeyMap(document, {
13882     key: Roo.EventObject.ENTER,
13883     fn: handleKey,
13884     scope: this
13885 });
13886
13887 //Add a new binding to the existing KeyMap later
13888 map.addBinding({
13889     key: 'abc',
13890     shift: true,
13891     fn: handleKey,
13892     scope: this
13893 });
13894 </code></pre>
13895      * @param {Object/Array} config A single KeyMap config or an array of configs
13896      */
13897         addBinding : function(config){
13898         if(config instanceof Array){
13899             for(var i = 0, len = config.length; i < len; i++){
13900                 this.addBinding(config[i]);
13901             }
13902             return;
13903         }
13904         var keyCode = config.key,
13905             shift = config.shift, 
13906             ctrl = config.ctrl, 
13907             alt = config.alt,
13908             fn = config.fn,
13909             scope = config.scope;
13910         if(typeof keyCode == "string"){
13911             var ks = [];
13912             var keyString = keyCode.toUpperCase();
13913             for(var j = 0, len = keyString.length; j < len; j++){
13914                 ks.push(keyString.charCodeAt(j));
13915             }
13916             keyCode = ks;
13917         }
13918         var keyArray = keyCode instanceof Array;
13919         var handler = function(e){
13920             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13921                 var k = e.getKey();
13922                 if(keyArray){
13923                     for(var i = 0, len = keyCode.length; i < len; i++){
13924                         if(keyCode[i] == k){
13925                           if(this.stopEvent){
13926                               e.stopEvent();
13927                           }
13928                           fn.call(scope || window, k, e);
13929                           return;
13930                         }
13931                     }
13932                 }else{
13933                     if(k == keyCode){
13934                         if(this.stopEvent){
13935                            e.stopEvent();
13936                         }
13937                         fn.call(scope || window, k, e);
13938                     }
13939                 }
13940             }
13941         };
13942         this.bindings.push(handler);  
13943         },
13944
13945     /**
13946      * Shorthand for adding a single key listener
13947      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13948      * following options:
13949      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13950      * @param {Function} fn The function to call
13951      * @param {Object} scope (optional) The scope of the function
13952      */
13953     on : function(key, fn, scope){
13954         var keyCode, shift, ctrl, alt;
13955         if(typeof key == "object" && !(key instanceof Array)){
13956             keyCode = key.key;
13957             shift = key.shift;
13958             ctrl = key.ctrl;
13959             alt = key.alt;
13960         }else{
13961             keyCode = key;
13962         }
13963         this.addBinding({
13964             key: keyCode,
13965             shift: shift,
13966             ctrl: ctrl,
13967             alt: alt,
13968             fn: fn,
13969             scope: scope
13970         })
13971     },
13972
13973     // private
13974     handleKeyDown : function(e){
13975             if(this.enabled){ //just in case
13976             var b = this.bindings;
13977             for(var i = 0, len = b.length; i < len; i++){
13978                 b[i].call(this, e);
13979             }
13980             }
13981         },
13982         
13983         /**
13984          * Returns true if this KeyMap is enabled
13985          * @return {Boolean} 
13986          */
13987         isEnabled : function(){
13988             return this.enabled;  
13989         },
13990         
13991         /**
13992          * Enables this KeyMap
13993          */
13994         enable: function(){
13995                 if(!this.enabled){
13996                     this.el.on(this.eventName, this.handleKeyDown, this);
13997                     this.enabled = true;
13998                 }
13999         },
14000
14001         /**
14002          * Disable this KeyMap
14003          */
14004         disable: function(){
14005                 if(this.enabled){
14006                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14007                     this.enabled = false;
14008                 }
14009         }
14010 };/*
14011  * Based on:
14012  * Ext JS Library 1.1.1
14013  * Copyright(c) 2006-2007, Ext JS, LLC.
14014  *
14015  * Originally Released Under LGPL - original licence link has changed is not relivant.
14016  *
14017  * Fork - LGPL
14018  * <script type="text/javascript">
14019  */
14020
14021  
14022 /**
14023  * @class Roo.util.TextMetrics
14024  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14025  * wide, in pixels, a given block of text will be.
14026  * @singleton
14027  */
14028 Roo.util.TextMetrics = function(){
14029     var shared;
14030     return {
14031         /**
14032          * Measures the size of the specified text
14033          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14034          * that can affect the size of the rendered text
14035          * @param {String} text The text to measure
14036          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14037          * in order to accurately measure the text height
14038          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14039          */
14040         measure : function(el, text, fixedWidth){
14041             if(!shared){
14042                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14043             }
14044             shared.bind(el);
14045             shared.setFixedWidth(fixedWidth || 'auto');
14046             return shared.getSize(text);
14047         },
14048
14049         /**
14050          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14051          * the overhead of multiple calls to initialize the style properties on each measurement.
14052          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14053          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14054          * in order to accurately measure the text height
14055          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14056          */
14057         createInstance : function(el, fixedWidth){
14058             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14059         }
14060     };
14061 }();
14062
14063  
14064
14065 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14066     var ml = new Roo.Element(document.createElement('div'));
14067     document.body.appendChild(ml.dom);
14068     ml.position('absolute');
14069     ml.setLeftTop(-1000, -1000);
14070     ml.hide();
14071
14072     if(fixedWidth){
14073         ml.setWidth(fixedWidth);
14074     }
14075      
14076     var instance = {
14077         /**
14078          * Returns the size of the specified text based on the internal element's style and width properties
14079          * @memberOf Roo.util.TextMetrics.Instance#
14080          * @param {String} text The text to measure
14081          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14082          */
14083         getSize : function(text){
14084             ml.update(text);
14085             var s = ml.getSize();
14086             ml.update('');
14087             return s;
14088         },
14089
14090         /**
14091          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14092          * that can affect the size of the rendered text
14093          * @memberOf Roo.util.TextMetrics.Instance#
14094          * @param {String/HTMLElement} el The element, dom node or id
14095          */
14096         bind : function(el){
14097             ml.setStyle(
14098                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14099             );
14100         },
14101
14102         /**
14103          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14104          * to set a fixed width in order to accurately measure the text height.
14105          * @memberOf Roo.util.TextMetrics.Instance#
14106          * @param {Number} width The width to set on the element
14107          */
14108         setFixedWidth : function(width){
14109             ml.setWidth(width);
14110         },
14111
14112         /**
14113          * Returns the measured width of the specified text
14114          * @memberOf Roo.util.TextMetrics.Instance#
14115          * @param {String} text The text to measure
14116          * @return {Number} width The width in pixels
14117          */
14118         getWidth : function(text){
14119             ml.dom.style.width = 'auto';
14120             return this.getSize(text).width;
14121         },
14122
14123         /**
14124          * Returns the measured height of the specified text.  For multiline text, be sure to call
14125          * {@link #setFixedWidth} if necessary.
14126          * @memberOf Roo.util.TextMetrics.Instance#
14127          * @param {String} text The text to measure
14128          * @return {Number} height The height in pixels
14129          */
14130         getHeight : function(text){
14131             return this.getSize(text).height;
14132         }
14133     };
14134
14135     instance.bind(bindTo);
14136
14137     return instance;
14138 };
14139
14140 // backwards compat
14141 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14142  * Based on:
14143  * Ext JS Library 1.1.1
14144  * Copyright(c) 2006-2007, Ext JS, LLC.
14145  *
14146  * Originally Released Under LGPL - original licence link has changed is not relivant.
14147  *
14148  * Fork - LGPL
14149  * <script type="text/javascript">
14150  */
14151
14152 /**
14153  * @class Roo.state.Provider
14154  * Abstract base class for state provider implementations. This class provides methods
14155  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14156  * Provider interface.
14157  */
14158 Roo.state.Provider = function(){
14159     /**
14160      * @event statechange
14161      * Fires when a state change occurs.
14162      * @param {Provider} this This state provider
14163      * @param {String} key The state key which was changed
14164      * @param {String} value The encoded value for the state
14165      */
14166     this.addEvents({
14167         "statechange": true
14168     });
14169     this.state = {};
14170     Roo.state.Provider.superclass.constructor.call(this);
14171 };
14172 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14173     /**
14174      * Returns the current value for a key
14175      * @param {String} name The key name
14176      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14177      * @return {Mixed} The state data
14178      */
14179     get : function(name, defaultValue){
14180         return typeof this.state[name] == "undefined" ?
14181             defaultValue : this.state[name];
14182     },
14183     
14184     /**
14185      * Clears a value from the state
14186      * @param {String} name The key name
14187      */
14188     clear : function(name){
14189         delete this.state[name];
14190         this.fireEvent("statechange", this, name, null);
14191     },
14192     
14193     /**
14194      * Sets the value for a key
14195      * @param {String} name The key name
14196      * @param {Mixed} value The value to set
14197      */
14198     set : function(name, value){
14199         this.state[name] = value;
14200         this.fireEvent("statechange", this, name, value);
14201     },
14202     
14203     /**
14204      * Decodes a string previously encoded with {@link #encodeValue}.
14205      * @param {String} value The value to decode
14206      * @return {Mixed} The decoded value
14207      */
14208     decodeValue : function(cookie){
14209         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14210         var matches = re.exec(unescape(cookie));
14211         if(!matches || !matches[1]) return; // non state cookie
14212         var type = matches[1];
14213         var v = matches[2];
14214         switch(type){
14215             case "n":
14216                 return parseFloat(v);
14217             case "d":
14218                 return new Date(Date.parse(v));
14219             case "b":
14220                 return (v == "1");
14221             case "a":
14222                 var all = [];
14223                 var values = v.split("^");
14224                 for(var i = 0, len = values.length; i < len; i++){
14225                     all.push(this.decodeValue(values[i]));
14226                 }
14227                 return all;
14228            case "o":
14229                 var all = {};
14230                 var values = v.split("^");
14231                 for(var i = 0, len = values.length; i < len; i++){
14232                     var kv = values[i].split("=");
14233                     all[kv[0]] = this.decodeValue(kv[1]);
14234                 }
14235                 return all;
14236            default:
14237                 return v;
14238         }
14239     },
14240     
14241     /**
14242      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14243      * @param {Mixed} value The value to encode
14244      * @return {String} The encoded value
14245      */
14246     encodeValue : function(v){
14247         var enc;
14248         if(typeof v == "number"){
14249             enc = "n:" + v;
14250         }else if(typeof v == "boolean"){
14251             enc = "b:" + (v ? "1" : "0");
14252         }else if(v instanceof Date){
14253             enc = "d:" + v.toGMTString();
14254         }else if(v instanceof Array){
14255             var flat = "";
14256             for(var i = 0, len = v.length; i < len; i++){
14257                 flat += this.encodeValue(v[i]);
14258                 if(i != len-1) flat += "^";
14259             }
14260             enc = "a:" + flat;
14261         }else if(typeof v == "object"){
14262             var flat = "";
14263             for(var key in v){
14264                 if(typeof v[key] != "function"){
14265                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14266                 }
14267             }
14268             enc = "o:" + flat.substring(0, flat.length-1);
14269         }else{
14270             enc = "s:" + v;
14271         }
14272         return escape(enc);        
14273     }
14274 });
14275
14276 /*
14277  * Based on:
14278  * Ext JS Library 1.1.1
14279  * Copyright(c) 2006-2007, Ext JS, LLC.
14280  *
14281  * Originally Released Under LGPL - original licence link has changed is not relivant.
14282  *
14283  * Fork - LGPL
14284  * <script type="text/javascript">
14285  */
14286 /**
14287  * @class Roo.state.Manager
14288  * This is the global state manager. By default all components that are "state aware" check this class
14289  * for state information if you don't pass them a custom state provider. In order for this class
14290  * to be useful, it must be initialized with a provider when your application initializes.
14291  <pre><code>
14292 // in your initialization function
14293 init : function(){
14294    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14295    ...
14296    // supposed you have a {@link Roo.BorderLayout}
14297    var layout = new Roo.BorderLayout(...);
14298    layout.restoreState();
14299    // or a {Roo.BasicDialog}
14300    var dialog = new Roo.BasicDialog(...);
14301    dialog.restoreState();
14302  </code></pre>
14303  * @singleton
14304  */
14305 Roo.state.Manager = function(){
14306     var provider = new Roo.state.Provider();
14307     
14308     return {
14309         /**
14310          * Configures the default state provider for your application
14311          * @param {Provider} stateProvider The state provider to set
14312          */
14313         setProvider : function(stateProvider){
14314             provider = stateProvider;
14315         },
14316         
14317         /**
14318          * Returns the current value for a key
14319          * @param {String} name The key name
14320          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14321          * @return {Mixed} The state data
14322          */
14323         get : function(key, defaultValue){
14324             return provider.get(key, defaultValue);
14325         },
14326         
14327         /**
14328          * Sets the value for a key
14329          * @param {String} name The key name
14330          * @param {Mixed} value The state data
14331          */
14332          set : function(key, value){
14333             provider.set(key, value);
14334         },
14335         
14336         /**
14337          * Clears a value from the state
14338          * @param {String} name The key name
14339          */
14340         clear : function(key){
14341             provider.clear(key);
14342         },
14343         
14344         /**
14345          * Gets the currently configured state provider
14346          * @return {Provider} The state provider
14347          */
14348         getProvider : function(){
14349             return provider;
14350         }
14351     };
14352 }();
14353 /*
14354  * Based on:
14355  * Ext JS Library 1.1.1
14356  * Copyright(c) 2006-2007, Ext JS, LLC.
14357  *
14358  * Originally Released Under LGPL - original licence link has changed is not relivant.
14359  *
14360  * Fork - LGPL
14361  * <script type="text/javascript">
14362  */
14363 /**
14364  * @class Roo.state.CookieProvider
14365  * @extends Roo.state.Provider
14366  * The default Provider implementation which saves state via cookies.
14367  * <br />Usage:
14368  <pre><code>
14369    var cp = new Roo.state.CookieProvider({
14370        path: "/cgi-bin/",
14371        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14372        domain: "roojs.com"
14373    })
14374    Roo.state.Manager.setProvider(cp);
14375  </code></pre>
14376  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14377  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14378  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14379  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14380  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14381  * domain the page is running on including the 'www' like 'www.roojs.com')
14382  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14383  * @constructor
14384  * Create a new CookieProvider
14385  * @param {Object} config The configuration object
14386  */
14387 Roo.state.CookieProvider = function(config){
14388     Roo.state.CookieProvider.superclass.constructor.call(this);
14389     this.path = "/";
14390     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14391     this.domain = null;
14392     this.secure = false;
14393     Roo.apply(this, config);
14394     this.state = this.readCookies();
14395 };
14396
14397 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14398     // private
14399     set : function(name, value){
14400         if(typeof value == "undefined" || value === null){
14401             this.clear(name);
14402             return;
14403         }
14404         this.setCookie(name, value);
14405         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14406     },
14407
14408     // private
14409     clear : function(name){
14410         this.clearCookie(name);
14411         Roo.state.CookieProvider.superclass.clear.call(this, name);
14412     },
14413
14414     // private
14415     readCookies : function(){
14416         var cookies = {};
14417         var c = document.cookie + ";";
14418         var re = /\s?(.*?)=(.*?);/g;
14419         var matches;
14420         while((matches = re.exec(c)) != null){
14421             var name = matches[1];
14422             var value = matches[2];
14423             if(name && name.substring(0,3) == "ys-"){
14424                 cookies[name.substr(3)] = this.decodeValue(value);
14425             }
14426         }
14427         return cookies;
14428     },
14429
14430     // private
14431     setCookie : function(name, value){
14432         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14433            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14434            ((this.path == null) ? "" : ("; path=" + this.path)) +
14435            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14436            ((this.secure == true) ? "; secure" : "");
14437     },
14438
14439     // private
14440     clearCookie : function(name){
14441         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14442            ((this.path == null) ? "" : ("; path=" + this.path)) +
14443            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14444            ((this.secure == true) ? "; secure" : "");
14445     }
14446 });/*
14447  * Based on:
14448  * Ext JS Library 1.1.1
14449  * Copyright(c) 2006-2007, Ext JS, LLC.
14450  *
14451  * Originally Released Under LGPL - original licence link has changed is not relivant.
14452  *
14453  * Fork - LGPL
14454  * <script type="text/javascript">
14455  */
14456
14457
14458
14459 /*
14460  * These classes are derivatives of the similarly named classes in the YUI Library.
14461  * The original license:
14462  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14463  * Code licensed under the BSD License:
14464  * http://developer.yahoo.net/yui/license.txt
14465  */
14466
14467 (function() {
14468
14469 var Event=Roo.EventManager;
14470 var Dom=Roo.lib.Dom;
14471
14472 /**
14473  * @class Roo.dd.DragDrop
14474  * @extends Roo.util.Observable
14475  * Defines the interface and base operation of items that that can be
14476  * dragged or can be drop targets.  It was designed to be extended, overriding
14477  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14478  * Up to three html elements can be associated with a DragDrop instance:
14479  * <ul>
14480  * <li>linked element: the element that is passed into the constructor.
14481  * This is the element which defines the boundaries for interaction with
14482  * other DragDrop objects.</li>
14483  * <li>handle element(s): The drag operation only occurs if the element that
14484  * was clicked matches a handle element.  By default this is the linked
14485  * element, but there are times that you will want only a portion of the
14486  * linked element to initiate the drag operation, and the setHandleElId()
14487  * method provides a way to define this.</li>
14488  * <li>drag element: this represents the element that would be moved along
14489  * with the cursor during a drag operation.  By default, this is the linked
14490  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14491  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14492  * </li>
14493  * </ul>
14494  * This class should not be instantiated until the onload event to ensure that
14495  * the associated elements are available.
14496  * The following would define a DragDrop obj that would interact with any
14497  * other DragDrop obj in the "group1" group:
14498  * <pre>
14499  *  dd = new Roo.dd.DragDrop("div1", "group1");
14500  * </pre>
14501  * Since none of the event handlers have been implemented, nothing would
14502  * actually happen if you were to run the code above.  Normally you would
14503  * override this class or one of the default implementations, but you can
14504  * also override the methods you want on an instance of the class...
14505  * <pre>
14506  *  dd.onDragDrop = function(e, id) {
14507  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14508  *  }
14509  * </pre>
14510  * @constructor
14511  * @param {String} id of the element that is linked to this instance
14512  * @param {String} sGroup the group of related DragDrop objects
14513  * @param {object} config an object containing configurable attributes
14514  *                Valid properties for DragDrop:
14515  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14516  */
14517 Roo.dd.DragDrop = function(id, sGroup, config) {
14518     if (id) {
14519         this.init(id, sGroup, config);
14520     }
14521     
14522 };
14523
14524 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14525
14526     /**
14527      * The id of the element associated with this object.  This is what we
14528      * refer to as the "linked element" because the size and position of
14529      * this element is used to determine when the drag and drop objects have
14530      * interacted.
14531      * @property id
14532      * @type String
14533      */
14534     id: null,
14535
14536     /**
14537      * Configuration attributes passed into the constructor
14538      * @property config
14539      * @type object
14540      */
14541     config: null,
14542
14543     /**
14544      * The id of the element that will be dragged.  By default this is same
14545      * as the linked element , but could be changed to another element. Ex:
14546      * Roo.dd.DDProxy
14547      * @property dragElId
14548      * @type String
14549      * @private
14550      */
14551     dragElId: null,
14552
14553     /**
14554      * the id of the element that initiates the drag operation.  By default
14555      * this is the linked element, but could be changed to be a child of this
14556      * element.  This lets us do things like only starting the drag when the
14557      * header element within the linked html element is clicked.
14558      * @property handleElId
14559      * @type String
14560      * @private
14561      */
14562     handleElId: null,
14563
14564     /**
14565      * An associative array of HTML tags that will be ignored if clicked.
14566      * @property invalidHandleTypes
14567      * @type {string: string}
14568      */
14569     invalidHandleTypes: null,
14570
14571     /**
14572      * An associative array of ids for elements that will be ignored if clicked
14573      * @property invalidHandleIds
14574      * @type {string: string}
14575      */
14576     invalidHandleIds: null,
14577
14578     /**
14579      * An indexted array of css class names for elements that will be ignored
14580      * if clicked.
14581      * @property invalidHandleClasses
14582      * @type string[]
14583      */
14584     invalidHandleClasses: null,
14585
14586     /**
14587      * The linked element's absolute X position at the time the drag was
14588      * started
14589      * @property startPageX
14590      * @type int
14591      * @private
14592      */
14593     startPageX: 0,
14594
14595     /**
14596      * The linked element's absolute X position at the time the drag was
14597      * started
14598      * @property startPageY
14599      * @type int
14600      * @private
14601      */
14602     startPageY: 0,
14603
14604     /**
14605      * The group defines a logical collection of DragDrop objects that are
14606      * related.  Instances only get events when interacting with other
14607      * DragDrop object in the same group.  This lets us define multiple
14608      * groups using a single DragDrop subclass if we want.
14609      * @property groups
14610      * @type {string: string}
14611      */
14612     groups: null,
14613
14614     /**
14615      * Individual drag/drop instances can be locked.  This will prevent
14616      * onmousedown start drag.
14617      * @property locked
14618      * @type boolean
14619      * @private
14620      */
14621     locked: false,
14622
14623     /**
14624      * Lock this instance
14625      * @method lock
14626      */
14627     lock: function() { this.locked = true; },
14628
14629     /**
14630      * Unlock this instace
14631      * @method unlock
14632      */
14633     unlock: function() { this.locked = false; },
14634
14635     /**
14636      * By default, all insances can be a drop target.  This can be disabled by
14637      * setting isTarget to false.
14638      * @method isTarget
14639      * @type boolean
14640      */
14641     isTarget: true,
14642
14643     /**
14644      * The padding configured for this drag and drop object for calculating
14645      * the drop zone intersection with this object.
14646      * @method padding
14647      * @type int[]
14648      */
14649     padding: null,
14650
14651     /**
14652      * Cached reference to the linked element
14653      * @property _domRef
14654      * @private
14655      */
14656     _domRef: null,
14657
14658     /**
14659      * Internal typeof flag
14660      * @property __ygDragDrop
14661      * @private
14662      */
14663     __ygDragDrop: true,
14664
14665     /**
14666      * Set to true when horizontal contraints are applied
14667      * @property constrainX
14668      * @type boolean
14669      * @private
14670      */
14671     constrainX: false,
14672
14673     /**
14674      * Set to true when vertical contraints are applied
14675      * @property constrainY
14676      * @type boolean
14677      * @private
14678      */
14679     constrainY: false,
14680
14681     /**
14682      * The left constraint
14683      * @property minX
14684      * @type int
14685      * @private
14686      */
14687     minX: 0,
14688
14689     /**
14690      * The right constraint
14691      * @property maxX
14692      * @type int
14693      * @private
14694      */
14695     maxX: 0,
14696
14697     /**
14698      * The up constraint
14699      * @property minY
14700      * @type int
14701      * @type int
14702      * @private
14703      */
14704     minY: 0,
14705
14706     /**
14707      * The down constraint
14708      * @property maxY
14709      * @type int
14710      * @private
14711      */
14712     maxY: 0,
14713
14714     /**
14715      * Maintain offsets when we resetconstraints.  Set to true when you want
14716      * the position of the element relative to its parent to stay the same
14717      * when the page changes
14718      *
14719      * @property maintainOffset
14720      * @type boolean
14721      */
14722     maintainOffset: false,
14723
14724     /**
14725      * Array of pixel locations the element will snap to if we specified a
14726      * horizontal graduation/interval.  This array is generated automatically
14727      * when you define a tick interval.
14728      * @property xTicks
14729      * @type int[]
14730      */
14731     xTicks: null,
14732
14733     /**
14734      * Array of pixel locations the element will snap to if we specified a
14735      * vertical graduation/interval.  This array is generated automatically
14736      * when you define a tick interval.
14737      * @property yTicks
14738      * @type int[]
14739      */
14740     yTicks: null,
14741
14742     /**
14743      * By default the drag and drop instance will only respond to the primary
14744      * button click (left button for a right-handed mouse).  Set to true to
14745      * allow drag and drop to start with any mouse click that is propogated
14746      * by the browser
14747      * @property primaryButtonOnly
14748      * @type boolean
14749      */
14750     primaryButtonOnly: true,
14751
14752     /**
14753      * The availabe property is false until the linked dom element is accessible.
14754      * @property available
14755      * @type boolean
14756      */
14757     available: false,
14758
14759     /**
14760      * By default, drags can only be initiated if the mousedown occurs in the
14761      * region the linked element is.  This is done in part to work around a
14762      * bug in some browsers that mis-report the mousedown if the previous
14763      * mouseup happened outside of the window.  This property is set to true
14764      * if outer handles are defined.
14765      *
14766      * @property hasOuterHandles
14767      * @type boolean
14768      * @default false
14769      */
14770     hasOuterHandles: false,
14771
14772     /**
14773      * Code that executes immediately before the startDrag event
14774      * @method b4StartDrag
14775      * @private
14776      */
14777     b4StartDrag: function(x, y) { },
14778
14779     /**
14780      * Abstract method called after a drag/drop object is clicked
14781      * and the drag or mousedown time thresholds have beeen met.
14782      * @method startDrag
14783      * @param {int} X click location
14784      * @param {int} Y click location
14785      */
14786     startDrag: function(x, y) { /* override this */ },
14787
14788     /**
14789      * Code that executes immediately before the onDrag event
14790      * @method b4Drag
14791      * @private
14792      */
14793     b4Drag: function(e) { },
14794
14795     /**
14796      * Abstract method called during the onMouseMove event while dragging an
14797      * object.
14798      * @method onDrag
14799      * @param {Event} e the mousemove event
14800      */
14801     onDrag: function(e) { /* override this */ },
14802
14803     /**
14804      * Abstract method called when this element fist begins hovering over
14805      * another DragDrop obj
14806      * @method onDragEnter
14807      * @param {Event} e the mousemove event
14808      * @param {String|DragDrop[]} id In POINT mode, the element
14809      * id this is hovering over.  In INTERSECT mode, an array of one or more
14810      * dragdrop items being hovered over.
14811      */
14812     onDragEnter: function(e, id) { /* override this */ },
14813
14814     /**
14815      * Code that executes immediately before the onDragOver event
14816      * @method b4DragOver
14817      * @private
14818      */
14819     b4DragOver: function(e) { },
14820
14821     /**
14822      * Abstract method called when this element is hovering over another
14823      * DragDrop obj
14824      * @method onDragOver
14825      * @param {Event} e the mousemove event
14826      * @param {String|DragDrop[]} id In POINT mode, the element
14827      * id this is hovering over.  In INTERSECT mode, an array of dd items
14828      * being hovered over.
14829      */
14830     onDragOver: function(e, id) { /* override this */ },
14831
14832     /**
14833      * Code that executes immediately before the onDragOut event
14834      * @method b4DragOut
14835      * @private
14836      */
14837     b4DragOut: function(e) { },
14838
14839     /**
14840      * Abstract method called when we are no longer hovering over an element
14841      * @method onDragOut
14842      * @param {Event} e the mousemove event
14843      * @param {String|DragDrop[]} id In POINT mode, the element
14844      * id this was hovering over.  In INTERSECT mode, an array of dd items
14845      * that the mouse is no longer over.
14846      */
14847     onDragOut: function(e, id) { /* override this */ },
14848
14849     /**
14850      * Code that executes immediately before the onDragDrop event
14851      * @method b4DragDrop
14852      * @private
14853      */
14854     b4DragDrop: function(e) { },
14855
14856     /**
14857      * Abstract method called when this item is dropped on another DragDrop
14858      * obj
14859      * @method onDragDrop
14860      * @param {Event} e the mouseup event
14861      * @param {String|DragDrop[]} id In POINT mode, the element
14862      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14863      * was dropped on.
14864      */
14865     onDragDrop: function(e, id) { /* override this */ },
14866
14867     /**
14868      * Abstract method called when this item is dropped on an area with no
14869      * drop target
14870      * @method onInvalidDrop
14871      * @param {Event} e the mouseup event
14872      */
14873     onInvalidDrop: function(e) { /* override this */ },
14874
14875     /**
14876      * Code that executes immediately before the endDrag event
14877      * @method b4EndDrag
14878      * @private
14879      */
14880     b4EndDrag: function(e) { },
14881
14882     /**
14883      * Fired when we are done dragging the object
14884      * @method endDrag
14885      * @param {Event} e the mouseup event
14886      */
14887     endDrag: function(e) { /* override this */ },
14888
14889     /**
14890      * Code executed immediately before the onMouseDown event
14891      * @method b4MouseDown
14892      * @param {Event} e the mousedown event
14893      * @private
14894      */
14895     b4MouseDown: function(e) {  },
14896
14897     /**
14898      * Event handler that fires when a drag/drop obj gets a mousedown
14899      * @method onMouseDown
14900      * @param {Event} e the mousedown event
14901      */
14902     onMouseDown: function(e) { /* override this */ },
14903
14904     /**
14905      * Event handler that fires when a drag/drop obj gets a mouseup
14906      * @method onMouseUp
14907      * @param {Event} e the mouseup event
14908      */
14909     onMouseUp: function(e) { /* override this */ },
14910
14911     /**
14912      * Override the onAvailable method to do what is needed after the initial
14913      * position was determined.
14914      * @method onAvailable
14915      */
14916     onAvailable: function () {
14917     },
14918
14919     /*
14920      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14921      * @type Object
14922      */
14923     defaultPadding : {left:0, right:0, top:0, bottom:0},
14924
14925     /*
14926      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14927  *
14928  * Usage:
14929  <pre><code>
14930  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14931                 { dragElId: "existingProxyDiv" });
14932  dd.startDrag = function(){
14933      this.constrainTo("parent-id");
14934  };
14935  </code></pre>
14936  * Or you can initalize it using the {@link Roo.Element} object:
14937  <pre><code>
14938  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14939      startDrag : function(){
14940          this.constrainTo("parent-id");
14941      }
14942  });
14943  </code></pre>
14944      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14945      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14946      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14947      * an object containing the sides to pad. For example: {right:10, bottom:10}
14948      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14949      */
14950     constrainTo : function(constrainTo, pad, inContent){
14951         if(typeof pad == "number"){
14952             pad = {left: pad, right:pad, top:pad, bottom:pad};
14953         }
14954         pad = pad || this.defaultPadding;
14955         var b = Roo.get(this.getEl()).getBox();
14956         var ce = Roo.get(constrainTo);
14957         var s = ce.getScroll();
14958         var c, cd = ce.dom;
14959         if(cd == document.body){
14960             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14961         }else{
14962             xy = ce.getXY();
14963             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14964         }
14965
14966
14967         var topSpace = b.y - c.y;
14968         var leftSpace = b.x - c.x;
14969
14970         this.resetConstraints();
14971         this.setXConstraint(leftSpace - (pad.left||0), // left
14972                 c.width - leftSpace - b.width - (pad.right||0) //right
14973         );
14974         this.setYConstraint(topSpace - (pad.top||0), //top
14975                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14976         );
14977     },
14978
14979     /**
14980      * Returns a reference to the linked element
14981      * @method getEl
14982      * @return {HTMLElement} the html element
14983      */
14984     getEl: function() {
14985         if (!this._domRef) {
14986             this._domRef = Roo.getDom(this.id);
14987         }
14988
14989         return this._domRef;
14990     },
14991
14992     /**
14993      * Returns a reference to the actual element to drag.  By default this is
14994      * the same as the html element, but it can be assigned to another
14995      * element. An example of this can be found in Roo.dd.DDProxy
14996      * @method getDragEl
14997      * @return {HTMLElement} the html element
14998      */
14999     getDragEl: function() {
15000         return Roo.getDom(this.dragElId);
15001     },
15002
15003     /**
15004      * Sets up the DragDrop object.  Must be called in the constructor of any
15005      * Roo.dd.DragDrop subclass
15006      * @method init
15007      * @param id the id of the linked element
15008      * @param {String} sGroup the group of related items
15009      * @param {object} config configuration attributes
15010      */
15011     init: function(id, sGroup, config) {
15012         this.initTarget(id, sGroup, config);
15013         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15014         // Event.on(this.id, "selectstart", Event.preventDefault);
15015     },
15016
15017     /**
15018      * Initializes Targeting functionality only... the object does not
15019      * get a mousedown handler.
15020      * @method initTarget
15021      * @param id the id of the linked element
15022      * @param {String} sGroup the group of related items
15023      * @param {object} config configuration attributes
15024      */
15025     initTarget: function(id, sGroup, config) {
15026
15027         // configuration attributes
15028         this.config = config || {};
15029
15030         // create a local reference to the drag and drop manager
15031         this.DDM = Roo.dd.DDM;
15032         // initialize the groups array
15033         this.groups = {};
15034
15035         // assume that we have an element reference instead of an id if the
15036         // parameter is not a string
15037         if (typeof id !== "string") {
15038             id = Roo.id(id);
15039         }
15040
15041         // set the id
15042         this.id = id;
15043
15044         // add to an interaction group
15045         this.addToGroup((sGroup) ? sGroup : "default");
15046
15047         // We don't want to register this as the handle with the manager
15048         // so we just set the id rather than calling the setter.
15049         this.handleElId = id;
15050
15051         // the linked element is the element that gets dragged by default
15052         this.setDragElId(id);
15053
15054         // by default, clicked anchors will not start drag operations.
15055         this.invalidHandleTypes = { A: "A" };
15056         this.invalidHandleIds = {};
15057         this.invalidHandleClasses = [];
15058
15059         this.applyConfig();
15060
15061         this.handleOnAvailable();
15062     },
15063
15064     /**
15065      * Applies the configuration parameters that were passed into the constructor.
15066      * This is supposed to happen at each level through the inheritance chain.  So
15067      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15068      * DragDrop in order to get all of the parameters that are available in
15069      * each object.
15070      * @method applyConfig
15071      */
15072     applyConfig: function() {
15073
15074         // configurable properties:
15075         //    padding, isTarget, maintainOffset, primaryButtonOnly
15076         this.padding           = this.config.padding || [0, 0, 0, 0];
15077         this.isTarget          = (this.config.isTarget !== false);
15078         this.maintainOffset    = (this.config.maintainOffset);
15079         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15080
15081     },
15082
15083     /**
15084      * Executed when the linked element is available
15085      * @method handleOnAvailable
15086      * @private
15087      */
15088     handleOnAvailable: function() {
15089         this.available = true;
15090         this.resetConstraints();
15091         this.onAvailable();
15092     },
15093
15094      /**
15095      * Configures the padding for the target zone in px.  Effectively expands
15096      * (or reduces) the virtual object size for targeting calculations.
15097      * Supports css-style shorthand; if only one parameter is passed, all sides
15098      * will have that padding, and if only two are passed, the top and bottom
15099      * will have the first param, the left and right the second.
15100      * @method setPadding
15101      * @param {int} iTop    Top pad
15102      * @param {int} iRight  Right pad
15103      * @param {int} iBot    Bot pad
15104      * @param {int} iLeft   Left pad
15105      */
15106     setPadding: function(iTop, iRight, iBot, iLeft) {
15107         // this.padding = [iLeft, iRight, iTop, iBot];
15108         if (!iRight && 0 !== iRight) {
15109             this.padding = [iTop, iTop, iTop, iTop];
15110         } else if (!iBot && 0 !== iBot) {
15111             this.padding = [iTop, iRight, iTop, iRight];
15112         } else {
15113             this.padding = [iTop, iRight, iBot, iLeft];
15114         }
15115     },
15116
15117     /**
15118      * Stores the initial placement of the linked element.
15119      * @method setInitialPosition
15120      * @param {int} diffX   the X offset, default 0
15121      * @param {int} diffY   the Y offset, default 0
15122      */
15123     setInitPosition: function(diffX, diffY) {
15124         var el = this.getEl();
15125
15126         if (!this.DDM.verifyEl(el)) {
15127             return;
15128         }
15129
15130         var dx = diffX || 0;
15131         var dy = diffY || 0;
15132
15133         var p = Dom.getXY( el );
15134
15135         this.initPageX = p[0] - dx;
15136         this.initPageY = p[1] - dy;
15137
15138         this.lastPageX = p[0];
15139         this.lastPageY = p[1];
15140
15141
15142         this.setStartPosition(p);
15143     },
15144
15145     /**
15146      * Sets the start position of the element.  This is set when the obj
15147      * is initialized, the reset when a drag is started.
15148      * @method setStartPosition
15149      * @param pos current position (from previous lookup)
15150      * @private
15151      */
15152     setStartPosition: function(pos) {
15153         var p = pos || Dom.getXY( this.getEl() );
15154         this.deltaSetXY = null;
15155
15156         this.startPageX = p[0];
15157         this.startPageY = p[1];
15158     },
15159
15160     /**
15161      * Add this instance to a group of related drag/drop objects.  All
15162      * instances belong to at least one group, and can belong to as many
15163      * groups as needed.
15164      * @method addToGroup
15165      * @param sGroup {string} the name of the group
15166      */
15167     addToGroup: function(sGroup) {
15168         this.groups[sGroup] = true;
15169         this.DDM.regDragDrop(this, sGroup);
15170     },
15171
15172     /**
15173      * Remove's this instance from the supplied interaction group
15174      * @method removeFromGroup
15175      * @param {string}  sGroup  The group to drop
15176      */
15177     removeFromGroup: function(sGroup) {
15178         if (this.groups[sGroup]) {
15179             delete this.groups[sGroup];
15180         }
15181
15182         this.DDM.removeDDFromGroup(this, sGroup);
15183     },
15184
15185     /**
15186      * Allows you to specify that an element other than the linked element
15187      * will be moved with the cursor during a drag
15188      * @method setDragElId
15189      * @param id {string} the id of the element that will be used to initiate the drag
15190      */
15191     setDragElId: function(id) {
15192         this.dragElId = id;
15193     },
15194
15195     /**
15196      * Allows you to specify a child of the linked element that should be
15197      * used to initiate the drag operation.  An example of this would be if
15198      * you have a content div with text and links.  Clicking anywhere in the
15199      * content area would normally start the drag operation.  Use this method
15200      * to specify that an element inside of the content div is the element
15201      * that starts the drag operation.
15202      * @method setHandleElId
15203      * @param id {string} the id of the element that will be used to
15204      * initiate the drag.
15205      */
15206     setHandleElId: function(id) {
15207         if (typeof id !== "string") {
15208             id = Roo.id(id);
15209         }
15210         this.handleElId = id;
15211         this.DDM.regHandle(this.id, id);
15212     },
15213
15214     /**
15215      * Allows you to set an element outside of the linked element as a drag
15216      * handle
15217      * @method setOuterHandleElId
15218      * @param id the id of the element that will be used to initiate the drag
15219      */
15220     setOuterHandleElId: function(id) {
15221         if (typeof id !== "string") {
15222             id = Roo.id(id);
15223         }
15224         Event.on(id, "mousedown",
15225                 this.handleMouseDown, this);
15226         this.setHandleElId(id);
15227
15228         this.hasOuterHandles = true;
15229     },
15230
15231     /**
15232      * Remove all drag and drop hooks for this element
15233      * @method unreg
15234      */
15235     unreg: function() {
15236         Event.un(this.id, "mousedown",
15237                 this.handleMouseDown);
15238         this._domRef = null;
15239         this.DDM._remove(this);
15240     },
15241
15242     destroy : function(){
15243         this.unreg();
15244     },
15245
15246     /**
15247      * Returns true if this instance is locked, or the drag drop mgr is locked
15248      * (meaning that all drag/drop is disabled on the page.)
15249      * @method isLocked
15250      * @return {boolean} true if this obj or all drag/drop is locked, else
15251      * false
15252      */
15253     isLocked: function() {
15254         return (this.DDM.isLocked() || this.locked);
15255     },
15256
15257     /**
15258      * Fired when this object is clicked
15259      * @method handleMouseDown
15260      * @param {Event} e
15261      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15262      * @private
15263      */
15264     handleMouseDown: function(e, oDD){
15265         if (this.primaryButtonOnly && e.button != 0) {
15266             return;
15267         }
15268
15269         if (this.isLocked()) {
15270             return;
15271         }
15272
15273         this.DDM.refreshCache(this.groups);
15274
15275         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15276         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15277         } else {
15278             if (this.clickValidator(e)) {
15279
15280                 // set the initial element position
15281                 this.setStartPosition();
15282
15283
15284                 this.b4MouseDown(e);
15285                 this.onMouseDown(e);
15286
15287                 this.DDM.handleMouseDown(e, this);
15288
15289                 this.DDM.stopEvent(e);
15290             } else {
15291
15292
15293             }
15294         }
15295     },
15296
15297     clickValidator: function(e) {
15298         var target = e.getTarget();
15299         return ( this.isValidHandleChild(target) &&
15300                     (this.id == this.handleElId ||
15301                         this.DDM.handleWasClicked(target, this.id)) );
15302     },
15303
15304     /**
15305      * Allows you to specify a tag name that should not start a drag operation
15306      * when clicked.  This is designed to facilitate embedding links within a
15307      * drag handle that do something other than start the drag.
15308      * @method addInvalidHandleType
15309      * @param {string} tagName the type of element to exclude
15310      */
15311     addInvalidHandleType: function(tagName) {
15312         var type = tagName.toUpperCase();
15313         this.invalidHandleTypes[type] = type;
15314     },
15315
15316     /**
15317      * Lets you to specify an element id for a child of a drag handle
15318      * that should not initiate a drag
15319      * @method addInvalidHandleId
15320      * @param {string} id the element id of the element you wish to ignore
15321      */
15322     addInvalidHandleId: function(id) {
15323         if (typeof id !== "string") {
15324             id = Roo.id(id);
15325         }
15326         this.invalidHandleIds[id] = id;
15327     },
15328
15329     /**
15330      * Lets you specify a css class of elements that will not initiate a drag
15331      * @method addInvalidHandleClass
15332      * @param {string} cssClass the class of the elements you wish to ignore
15333      */
15334     addInvalidHandleClass: function(cssClass) {
15335         this.invalidHandleClasses.push(cssClass);
15336     },
15337
15338     /**
15339      * Unsets an excluded tag name set by addInvalidHandleType
15340      * @method removeInvalidHandleType
15341      * @param {string} tagName the type of element to unexclude
15342      */
15343     removeInvalidHandleType: function(tagName) {
15344         var type = tagName.toUpperCase();
15345         // this.invalidHandleTypes[type] = null;
15346         delete this.invalidHandleTypes[type];
15347     },
15348
15349     /**
15350      * Unsets an invalid handle id
15351      * @method removeInvalidHandleId
15352      * @param {string} id the id of the element to re-enable
15353      */
15354     removeInvalidHandleId: function(id) {
15355         if (typeof id !== "string") {
15356             id = Roo.id(id);
15357         }
15358         delete this.invalidHandleIds[id];
15359     },
15360
15361     /**
15362      * Unsets an invalid css class
15363      * @method removeInvalidHandleClass
15364      * @param {string} cssClass the class of the element(s) you wish to
15365      * re-enable
15366      */
15367     removeInvalidHandleClass: function(cssClass) {
15368         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15369             if (this.invalidHandleClasses[i] == cssClass) {
15370                 delete this.invalidHandleClasses[i];
15371             }
15372         }
15373     },
15374
15375     /**
15376      * Checks the tag exclusion list to see if this click should be ignored
15377      * @method isValidHandleChild
15378      * @param {HTMLElement} node the HTMLElement to evaluate
15379      * @return {boolean} true if this is a valid tag type, false if not
15380      */
15381     isValidHandleChild: function(node) {
15382
15383         var valid = true;
15384         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15385         var nodeName;
15386         try {
15387             nodeName = node.nodeName.toUpperCase();
15388         } catch(e) {
15389             nodeName = node.nodeName;
15390         }
15391         valid = valid && !this.invalidHandleTypes[nodeName];
15392         valid = valid && !this.invalidHandleIds[node.id];
15393
15394         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15395             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15396         }
15397
15398
15399         return valid;
15400
15401     },
15402
15403     /**
15404      * Create the array of horizontal tick marks if an interval was specified
15405      * in setXConstraint().
15406      * @method setXTicks
15407      * @private
15408      */
15409     setXTicks: function(iStartX, iTickSize) {
15410         this.xTicks = [];
15411         this.xTickSize = iTickSize;
15412
15413         var tickMap = {};
15414
15415         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15416             if (!tickMap[i]) {
15417                 this.xTicks[this.xTicks.length] = i;
15418                 tickMap[i] = true;
15419             }
15420         }
15421
15422         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15423             if (!tickMap[i]) {
15424                 this.xTicks[this.xTicks.length] = i;
15425                 tickMap[i] = true;
15426             }
15427         }
15428
15429         this.xTicks.sort(this.DDM.numericSort) ;
15430     },
15431
15432     /**
15433      * Create the array of vertical tick marks if an interval was specified in
15434      * setYConstraint().
15435      * @method setYTicks
15436      * @private
15437      */
15438     setYTicks: function(iStartY, iTickSize) {
15439         this.yTicks = [];
15440         this.yTickSize = iTickSize;
15441
15442         var tickMap = {};
15443
15444         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15445             if (!tickMap[i]) {
15446                 this.yTicks[this.yTicks.length] = i;
15447                 tickMap[i] = true;
15448             }
15449         }
15450
15451         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15452             if (!tickMap[i]) {
15453                 this.yTicks[this.yTicks.length] = i;
15454                 tickMap[i] = true;
15455             }
15456         }
15457
15458         this.yTicks.sort(this.DDM.numericSort) ;
15459     },
15460
15461     /**
15462      * By default, the element can be dragged any place on the screen.  Use
15463      * this method to limit the horizontal travel of the element.  Pass in
15464      * 0,0 for the parameters if you want to lock the drag to the y axis.
15465      * @method setXConstraint
15466      * @param {int} iLeft the number of pixels the element can move to the left
15467      * @param {int} iRight the number of pixels the element can move to the
15468      * right
15469      * @param {int} iTickSize optional parameter for specifying that the
15470      * element
15471      * should move iTickSize pixels at a time.
15472      */
15473     setXConstraint: function(iLeft, iRight, iTickSize) {
15474         this.leftConstraint = iLeft;
15475         this.rightConstraint = iRight;
15476
15477         this.minX = this.initPageX - iLeft;
15478         this.maxX = this.initPageX + iRight;
15479         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15480
15481         this.constrainX = true;
15482     },
15483
15484     /**
15485      * Clears any constraints applied to this instance.  Also clears ticks
15486      * since they can't exist independent of a constraint at this time.
15487      * @method clearConstraints
15488      */
15489     clearConstraints: function() {
15490         this.constrainX = false;
15491         this.constrainY = false;
15492         this.clearTicks();
15493     },
15494
15495     /**
15496      * Clears any tick interval defined for this instance
15497      * @method clearTicks
15498      */
15499     clearTicks: function() {
15500         this.xTicks = null;
15501         this.yTicks = null;
15502         this.xTickSize = 0;
15503         this.yTickSize = 0;
15504     },
15505
15506     /**
15507      * By default, the element can be dragged any place on the screen.  Set
15508      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15509      * parameters if you want to lock the drag to the x axis.
15510      * @method setYConstraint
15511      * @param {int} iUp the number of pixels the element can move up
15512      * @param {int} iDown the number of pixels the element can move down
15513      * @param {int} iTickSize optional parameter for specifying that the
15514      * element should move iTickSize pixels at a time.
15515      */
15516     setYConstraint: function(iUp, iDown, iTickSize) {
15517         this.topConstraint = iUp;
15518         this.bottomConstraint = iDown;
15519
15520         this.minY = this.initPageY - iUp;
15521         this.maxY = this.initPageY + iDown;
15522         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15523
15524         this.constrainY = true;
15525
15526     },
15527
15528     /**
15529      * resetConstraints must be called if you manually reposition a dd element.
15530      * @method resetConstraints
15531      * @param {boolean} maintainOffset
15532      */
15533     resetConstraints: function() {
15534
15535
15536         // Maintain offsets if necessary
15537         if (this.initPageX || this.initPageX === 0) {
15538             // figure out how much this thing has moved
15539             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15540             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15541
15542             this.setInitPosition(dx, dy);
15543
15544         // This is the first time we have detected the element's position
15545         } else {
15546             this.setInitPosition();
15547         }
15548
15549         if (this.constrainX) {
15550             this.setXConstraint( this.leftConstraint,
15551                                  this.rightConstraint,
15552                                  this.xTickSize        );
15553         }
15554
15555         if (this.constrainY) {
15556             this.setYConstraint( this.topConstraint,
15557                                  this.bottomConstraint,
15558                                  this.yTickSize         );
15559         }
15560     },
15561
15562     /**
15563      * Normally the drag element is moved pixel by pixel, but we can specify
15564      * that it move a number of pixels at a time.  This method resolves the
15565      * location when we have it set up like this.
15566      * @method getTick
15567      * @param {int} val where we want to place the object
15568      * @param {int[]} tickArray sorted array of valid points
15569      * @return {int} the closest tick
15570      * @private
15571      */
15572     getTick: function(val, tickArray) {
15573
15574         if (!tickArray) {
15575             // If tick interval is not defined, it is effectively 1 pixel,
15576             // so we return the value passed to us.
15577             return val;
15578         } else if (tickArray[0] >= val) {
15579             // The value is lower than the first tick, so we return the first
15580             // tick.
15581             return tickArray[0];
15582         } else {
15583             for (var i=0, len=tickArray.length; i<len; ++i) {
15584                 var next = i + 1;
15585                 if (tickArray[next] && tickArray[next] >= val) {
15586                     var diff1 = val - tickArray[i];
15587                     var diff2 = tickArray[next] - val;
15588                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15589                 }
15590             }
15591
15592             // The value is larger than the last tick, so we return the last
15593             // tick.
15594             return tickArray[tickArray.length - 1];
15595         }
15596     },
15597
15598     /**
15599      * toString method
15600      * @method toString
15601      * @return {string} string representation of the dd obj
15602      */
15603     toString: function() {
15604         return ("DragDrop " + this.id);
15605     }
15606
15607 });
15608
15609 })();
15610 /*
15611  * Based on:
15612  * Ext JS Library 1.1.1
15613  * Copyright(c) 2006-2007, Ext JS, LLC.
15614  *
15615  * Originally Released Under LGPL - original licence link has changed is not relivant.
15616  *
15617  * Fork - LGPL
15618  * <script type="text/javascript">
15619  */
15620
15621
15622 /**
15623  * The drag and drop utility provides a framework for building drag and drop
15624  * applications.  In addition to enabling drag and drop for specific elements,
15625  * the drag and drop elements are tracked by the manager class, and the
15626  * interactions between the various elements are tracked during the drag and
15627  * the implementing code is notified about these important moments.
15628  */
15629
15630 // Only load the library once.  Rewriting the manager class would orphan
15631 // existing drag and drop instances.
15632 if (!Roo.dd.DragDropMgr) {
15633
15634 /**
15635  * @class Roo.dd.DragDropMgr
15636  * DragDropMgr is a singleton that tracks the element interaction for
15637  * all DragDrop items in the window.  Generally, you will not call
15638  * this class directly, but it does have helper methods that could
15639  * be useful in your DragDrop implementations.
15640  * @singleton
15641  */
15642 Roo.dd.DragDropMgr = function() {
15643
15644     var Event = Roo.EventManager;
15645
15646     return {
15647
15648         /**
15649          * Two dimensional Array of registered DragDrop objects.  The first
15650          * dimension is the DragDrop item group, the second the DragDrop
15651          * object.
15652          * @property ids
15653          * @type {string: string}
15654          * @private
15655          * @static
15656          */
15657         ids: {},
15658
15659         /**
15660          * Array of element ids defined as drag handles.  Used to determine
15661          * if the element that generated the mousedown event is actually the
15662          * handle and not the html element itself.
15663          * @property handleIds
15664          * @type {string: string}
15665          * @private
15666          * @static
15667          */
15668         handleIds: {},
15669
15670         /**
15671          * the DragDrop object that is currently being dragged
15672          * @property dragCurrent
15673          * @type DragDrop
15674          * @private
15675          * @static
15676          **/
15677         dragCurrent: null,
15678
15679         /**
15680          * the DragDrop object(s) that are being hovered over
15681          * @property dragOvers
15682          * @type Array
15683          * @private
15684          * @static
15685          */
15686         dragOvers: {},
15687
15688         /**
15689          * the X distance between the cursor and the object being dragged
15690          * @property deltaX
15691          * @type int
15692          * @private
15693          * @static
15694          */
15695         deltaX: 0,
15696
15697         /**
15698          * the Y distance between the cursor and the object being dragged
15699          * @property deltaY
15700          * @type int
15701          * @private
15702          * @static
15703          */
15704         deltaY: 0,
15705
15706         /**
15707          * Flag to determine if we should prevent the default behavior of the
15708          * events we define. By default this is true, but this can be set to
15709          * false if you need the default behavior (not recommended)
15710          * @property preventDefault
15711          * @type boolean
15712          * @static
15713          */
15714         preventDefault: true,
15715
15716         /**
15717          * Flag to determine if we should stop the propagation of the events
15718          * we generate. This is true by default but you may want to set it to
15719          * false if the html element contains other features that require the
15720          * mouse click.
15721          * @property stopPropagation
15722          * @type boolean
15723          * @static
15724          */
15725         stopPropagation: true,
15726
15727         /**
15728          * Internal flag that is set to true when drag and drop has been
15729          * intialized
15730          * @property initialized
15731          * @private
15732          * @static
15733          */
15734         initalized: false,
15735
15736         /**
15737          * All drag and drop can be disabled.
15738          * @property locked
15739          * @private
15740          * @static
15741          */
15742         locked: false,
15743
15744         /**
15745          * Called the first time an element is registered.
15746          * @method init
15747          * @private
15748          * @static
15749          */
15750         init: function() {
15751             this.initialized = true;
15752         },
15753
15754         /**
15755          * In point mode, drag and drop interaction is defined by the
15756          * location of the cursor during the drag/drop
15757          * @property POINT
15758          * @type int
15759          * @static
15760          */
15761         POINT: 0,
15762
15763         /**
15764          * In intersect mode, drag and drop interactio nis defined by the
15765          * overlap of two or more drag and drop objects.
15766          * @property INTERSECT
15767          * @type int
15768          * @static
15769          */
15770         INTERSECT: 1,
15771
15772         /**
15773          * The current drag and drop mode.  Default: POINT
15774          * @property mode
15775          * @type int
15776          * @static
15777          */
15778         mode: 0,
15779
15780         /**
15781          * Runs method on all drag and drop objects
15782          * @method _execOnAll
15783          * @private
15784          * @static
15785          */
15786         _execOnAll: function(sMethod, args) {
15787             for (var i in this.ids) {
15788                 for (var j in this.ids[i]) {
15789                     var oDD = this.ids[i][j];
15790                     if (! this.isTypeOfDD(oDD)) {
15791                         continue;
15792                     }
15793                     oDD[sMethod].apply(oDD, args);
15794                 }
15795             }
15796         },
15797
15798         /**
15799          * Drag and drop initialization.  Sets up the global event handlers
15800          * @method _onLoad
15801          * @private
15802          * @static
15803          */
15804         _onLoad: function() {
15805
15806             this.init();
15807
15808
15809             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15810             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15811             Event.on(window,   "unload",    this._onUnload, this, true);
15812             Event.on(window,   "resize",    this._onResize, this, true);
15813             // Event.on(window,   "mouseout",    this._test);
15814
15815         },
15816
15817         /**
15818          * Reset constraints on all drag and drop objs
15819          * @method _onResize
15820          * @private
15821          * @static
15822          */
15823         _onResize: function(e) {
15824             this._execOnAll("resetConstraints", []);
15825         },
15826
15827         /**
15828          * Lock all drag and drop functionality
15829          * @method lock
15830          * @static
15831          */
15832         lock: function() { this.locked = true; },
15833
15834         /**
15835          * Unlock all drag and drop functionality
15836          * @method unlock
15837          * @static
15838          */
15839         unlock: function() { this.locked = false; },
15840
15841         /**
15842          * Is drag and drop locked?
15843          * @method isLocked
15844          * @return {boolean} True if drag and drop is locked, false otherwise.
15845          * @static
15846          */
15847         isLocked: function() { return this.locked; },
15848
15849         /**
15850          * Location cache that is set for all drag drop objects when a drag is
15851          * initiated, cleared when the drag is finished.
15852          * @property locationCache
15853          * @private
15854          * @static
15855          */
15856         locationCache: {},
15857
15858         /**
15859          * Set useCache to false if you want to force object the lookup of each
15860          * drag and drop linked element constantly during a drag.
15861          * @property useCache
15862          * @type boolean
15863          * @static
15864          */
15865         useCache: true,
15866
15867         /**
15868          * The number of pixels that the mouse needs to move after the
15869          * mousedown before the drag is initiated.  Default=3;
15870          * @property clickPixelThresh
15871          * @type int
15872          * @static
15873          */
15874         clickPixelThresh: 3,
15875
15876         /**
15877          * The number of milliseconds after the mousedown event to initiate the
15878          * drag if we don't get a mouseup event. Default=1000
15879          * @property clickTimeThresh
15880          * @type int
15881          * @static
15882          */
15883         clickTimeThresh: 350,
15884
15885         /**
15886          * Flag that indicates that either the drag pixel threshold or the
15887          * mousdown time threshold has been met
15888          * @property dragThreshMet
15889          * @type boolean
15890          * @private
15891          * @static
15892          */
15893         dragThreshMet: false,
15894
15895         /**
15896          * Timeout used for the click time threshold
15897          * @property clickTimeout
15898          * @type Object
15899          * @private
15900          * @static
15901          */
15902         clickTimeout: null,
15903
15904         /**
15905          * The X position of the mousedown event stored for later use when a
15906          * drag threshold is met.
15907          * @property startX
15908          * @type int
15909          * @private
15910          * @static
15911          */
15912         startX: 0,
15913
15914         /**
15915          * The Y position of the mousedown event stored for later use when a
15916          * drag threshold is met.
15917          * @property startY
15918          * @type int
15919          * @private
15920          * @static
15921          */
15922         startY: 0,
15923
15924         /**
15925          * Each DragDrop instance must be registered with the DragDropMgr.
15926          * This is executed in DragDrop.init()
15927          * @method regDragDrop
15928          * @param {DragDrop} oDD the DragDrop object to register
15929          * @param {String} sGroup the name of the group this element belongs to
15930          * @static
15931          */
15932         regDragDrop: function(oDD, sGroup) {
15933             if (!this.initialized) { this.init(); }
15934
15935             if (!this.ids[sGroup]) {
15936                 this.ids[sGroup] = {};
15937             }
15938             this.ids[sGroup][oDD.id] = oDD;
15939         },
15940
15941         /**
15942          * Removes the supplied dd instance from the supplied group. Executed
15943          * by DragDrop.removeFromGroup, so don't call this function directly.
15944          * @method removeDDFromGroup
15945          * @private
15946          * @static
15947          */
15948         removeDDFromGroup: function(oDD, sGroup) {
15949             if (!this.ids[sGroup]) {
15950                 this.ids[sGroup] = {};
15951             }
15952
15953             var obj = this.ids[sGroup];
15954             if (obj && obj[oDD.id]) {
15955                 delete obj[oDD.id];
15956             }
15957         },
15958
15959         /**
15960          * Unregisters a drag and drop item.  This is executed in
15961          * DragDrop.unreg, use that method instead of calling this directly.
15962          * @method _remove
15963          * @private
15964          * @static
15965          */
15966         _remove: function(oDD) {
15967             for (var g in oDD.groups) {
15968                 if (g && this.ids[g][oDD.id]) {
15969                     delete this.ids[g][oDD.id];
15970                 }
15971             }
15972             delete this.handleIds[oDD.id];
15973         },
15974
15975         /**
15976          * Each DragDrop handle element must be registered.  This is done
15977          * automatically when executing DragDrop.setHandleElId()
15978          * @method regHandle
15979          * @param {String} sDDId the DragDrop id this element is a handle for
15980          * @param {String} sHandleId the id of the element that is the drag
15981          * handle
15982          * @static
15983          */
15984         regHandle: function(sDDId, sHandleId) {
15985             if (!this.handleIds[sDDId]) {
15986                 this.handleIds[sDDId] = {};
15987             }
15988             this.handleIds[sDDId][sHandleId] = sHandleId;
15989         },
15990
15991         /**
15992          * Utility function to determine if a given element has been
15993          * registered as a drag drop item.
15994          * @method isDragDrop
15995          * @param {String} id the element id to check
15996          * @return {boolean} true if this element is a DragDrop item,
15997          * false otherwise
15998          * @static
15999          */
16000         isDragDrop: function(id) {
16001             return ( this.getDDById(id) ) ? true : false;
16002         },
16003
16004         /**
16005          * Returns the drag and drop instances that are in all groups the
16006          * passed in instance belongs to.
16007          * @method getRelated
16008          * @param {DragDrop} p_oDD the obj to get related data for
16009          * @param {boolean} bTargetsOnly if true, only return targetable objs
16010          * @return {DragDrop[]} the related instances
16011          * @static
16012          */
16013         getRelated: function(p_oDD, bTargetsOnly) {
16014             var oDDs = [];
16015             for (var i in p_oDD.groups) {
16016                 for (j in this.ids[i]) {
16017                     var dd = this.ids[i][j];
16018                     if (! this.isTypeOfDD(dd)) {
16019                         continue;
16020                     }
16021                     if (!bTargetsOnly || dd.isTarget) {
16022                         oDDs[oDDs.length] = dd;
16023                     }
16024                 }
16025             }
16026
16027             return oDDs;
16028         },
16029
16030         /**
16031          * Returns true if the specified dd target is a legal target for
16032          * the specifice drag obj
16033          * @method isLegalTarget
16034          * @param {DragDrop} the drag obj
16035          * @param {DragDrop} the target
16036          * @return {boolean} true if the target is a legal target for the
16037          * dd obj
16038          * @static
16039          */
16040         isLegalTarget: function (oDD, oTargetDD) {
16041             var targets = this.getRelated(oDD, true);
16042             for (var i=0, len=targets.length;i<len;++i) {
16043                 if (targets[i].id == oTargetDD.id) {
16044                     return true;
16045                 }
16046             }
16047
16048             return false;
16049         },
16050
16051         /**
16052          * My goal is to be able to transparently determine if an object is
16053          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16054          * returns "object", oDD.constructor.toString() always returns
16055          * "DragDrop" and not the name of the subclass.  So for now it just
16056          * evaluates a well-known variable in DragDrop.
16057          * @method isTypeOfDD
16058          * @param {Object} the object to evaluate
16059          * @return {boolean} true if typeof oDD = DragDrop
16060          * @static
16061          */
16062         isTypeOfDD: function (oDD) {
16063             return (oDD && oDD.__ygDragDrop);
16064         },
16065
16066         /**
16067          * Utility function to determine if a given element has been
16068          * registered as a drag drop handle for the given Drag Drop object.
16069          * @method isHandle
16070          * @param {String} id the element id to check
16071          * @return {boolean} true if this element is a DragDrop handle, false
16072          * otherwise
16073          * @static
16074          */
16075         isHandle: function(sDDId, sHandleId) {
16076             return ( this.handleIds[sDDId] &&
16077                             this.handleIds[sDDId][sHandleId] );
16078         },
16079
16080         /**
16081          * Returns the DragDrop instance for a given id
16082          * @method getDDById
16083          * @param {String} id the id of the DragDrop object
16084          * @return {DragDrop} the drag drop object, null if it is not found
16085          * @static
16086          */
16087         getDDById: function(id) {
16088             for (var i in this.ids) {
16089                 if (this.ids[i][id]) {
16090                     return this.ids[i][id];
16091                 }
16092             }
16093             return null;
16094         },
16095
16096         /**
16097          * Fired after a registered DragDrop object gets the mousedown event.
16098          * Sets up the events required to track the object being dragged
16099          * @method handleMouseDown
16100          * @param {Event} e the event
16101          * @param oDD the DragDrop object being dragged
16102          * @private
16103          * @static
16104          */
16105         handleMouseDown: function(e, oDD) {
16106             if(Roo.QuickTips){
16107                 Roo.QuickTips.disable();
16108             }
16109             this.currentTarget = e.getTarget();
16110
16111             this.dragCurrent = oDD;
16112
16113             var el = oDD.getEl();
16114
16115             // track start position
16116             this.startX = e.getPageX();
16117             this.startY = e.getPageY();
16118
16119             this.deltaX = this.startX - el.offsetLeft;
16120             this.deltaY = this.startY - el.offsetTop;
16121
16122             this.dragThreshMet = false;
16123
16124             this.clickTimeout = setTimeout(
16125                     function() {
16126                         var DDM = Roo.dd.DDM;
16127                         DDM.startDrag(DDM.startX, DDM.startY);
16128                     },
16129                     this.clickTimeThresh );
16130         },
16131
16132         /**
16133          * Fired when either the drag pixel threshol or the mousedown hold
16134          * time threshold has been met.
16135          * @method startDrag
16136          * @param x {int} the X position of the original mousedown
16137          * @param y {int} the Y position of the original mousedown
16138          * @static
16139          */
16140         startDrag: function(x, y) {
16141             clearTimeout(this.clickTimeout);
16142             if (this.dragCurrent) {
16143                 this.dragCurrent.b4StartDrag(x, y);
16144                 this.dragCurrent.startDrag(x, y);
16145             }
16146             this.dragThreshMet = true;
16147         },
16148
16149         /**
16150          * Internal function to handle the mouseup event.  Will be invoked
16151          * from the context of the document.
16152          * @method handleMouseUp
16153          * @param {Event} e the event
16154          * @private
16155          * @static
16156          */
16157         handleMouseUp: function(e) {
16158
16159             if(Roo.QuickTips){
16160                 Roo.QuickTips.enable();
16161             }
16162             if (! this.dragCurrent) {
16163                 return;
16164             }
16165
16166             clearTimeout(this.clickTimeout);
16167
16168             if (this.dragThreshMet) {
16169                 this.fireEvents(e, true);
16170             } else {
16171             }
16172
16173             this.stopDrag(e);
16174
16175             this.stopEvent(e);
16176         },
16177
16178         /**
16179          * Utility to stop event propagation and event default, if these
16180          * features are turned on.
16181          * @method stopEvent
16182          * @param {Event} e the event as returned by this.getEvent()
16183          * @static
16184          */
16185         stopEvent: function(e){
16186             if(this.stopPropagation) {
16187                 e.stopPropagation();
16188             }
16189
16190             if (this.preventDefault) {
16191                 e.preventDefault();
16192             }
16193         },
16194
16195         /**
16196          * Internal function to clean up event handlers after the drag
16197          * operation is complete
16198          * @method stopDrag
16199          * @param {Event} e the event
16200          * @private
16201          * @static
16202          */
16203         stopDrag: function(e) {
16204             // Fire the drag end event for the item that was dragged
16205             if (this.dragCurrent) {
16206                 if (this.dragThreshMet) {
16207                     this.dragCurrent.b4EndDrag(e);
16208                     this.dragCurrent.endDrag(e);
16209                 }
16210
16211                 this.dragCurrent.onMouseUp(e);
16212             }
16213
16214             this.dragCurrent = null;
16215             this.dragOvers = {};
16216         },
16217
16218         /**
16219          * Internal function to handle the mousemove event.  Will be invoked
16220          * from the context of the html element.
16221          *
16222          * @TODO figure out what we can do about mouse events lost when the
16223          * user drags objects beyond the window boundary.  Currently we can
16224          * detect this in internet explorer by verifying that the mouse is
16225          * down during the mousemove event.  Firefox doesn't give us the
16226          * button state on the mousemove event.
16227          * @method handleMouseMove
16228          * @param {Event} e the event
16229          * @private
16230          * @static
16231          */
16232         handleMouseMove: function(e) {
16233             if (! this.dragCurrent) {
16234                 return true;
16235             }
16236
16237             // var button = e.which || e.button;
16238
16239             // check for IE mouseup outside of page boundary
16240             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16241                 this.stopEvent(e);
16242                 return this.handleMouseUp(e);
16243             }
16244
16245             if (!this.dragThreshMet) {
16246                 var diffX = Math.abs(this.startX - e.getPageX());
16247                 var diffY = Math.abs(this.startY - e.getPageY());
16248                 if (diffX > this.clickPixelThresh ||
16249                             diffY > this.clickPixelThresh) {
16250                     this.startDrag(this.startX, this.startY);
16251                 }
16252             }
16253
16254             if (this.dragThreshMet) {
16255                 this.dragCurrent.b4Drag(e);
16256                 this.dragCurrent.onDrag(e);
16257                 if(!this.dragCurrent.moveOnly){
16258                     this.fireEvents(e, false);
16259                 }
16260             }
16261
16262             this.stopEvent(e);
16263
16264             return true;
16265         },
16266
16267         /**
16268          * Iterates over all of the DragDrop elements to find ones we are
16269          * hovering over or dropping on
16270          * @method fireEvents
16271          * @param {Event} e the event
16272          * @param {boolean} isDrop is this a drop op or a mouseover op?
16273          * @private
16274          * @static
16275          */
16276         fireEvents: function(e, isDrop) {
16277             var dc = this.dragCurrent;
16278
16279             // If the user did the mouse up outside of the window, we could
16280             // get here even though we have ended the drag.
16281             if (!dc || dc.isLocked()) {
16282                 return;
16283             }
16284
16285             var pt = e.getPoint();
16286
16287             // cache the previous dragOver array
16288             var oldOvers = [];
16289
16290             var outEvts   = [];
16291             var overEvts  = [];
16292             var dropEvts  = [];
16293             var enterEvts = [];
16294
16295             // Check to see if the object(s) we were hovering over is no longer
16296             // being hovered over so we can fire the onDragOut event
16297             for (var i in this.dragOvers) {
16298
16299                 var ddo = this.dragOvers[i];
16300
16301                 if (! this.isTypeOfDD(ddo)) {
16302                     continue;
16303                 }
16304
16305                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16306                     outEvts.push( ddo );
16307                 }
16308
16309                 oldOvers[i] = true;
16310                 delete this.dragOvers[i];
16311             }
16312
16313             for (var sGroup in dc.groups) {
16314
16315                 if ("string" != typeof sGroup) {
16316                     continue;
16317                 }
16318
16319                 for (i in this.ids[sGroup]) {
16320                     var oDD = this.ids[sGroup][i];
16321                     if (! this.isTypeOfDD(oDD)) {
16322                         continue;
16323                     }
16324
16325                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16326                         if (this.isOverTarget(pt, oDD, this.mode)) {
16327                             // look for drop interactions
16328                             if (isDrop) {
16329                                 dropEvts.push( oDD );
16330                             // look for drag enter and drag over interactions
16331                             } else {
16332
16333                                 // initial drag over: dragEnter fires
16334                                 if (!oldOvers[oDD.id]) {
16335                                     enterEvts.push( oDD );
16336                                 // subsequent drag overs: dragOver fires
16337                                 } else {
16338                                     overEvts.push( oDD );
16339                                 }
16340
16341                                 this.dragOvers[oDD.id] = oDD;
16342                             }
16343                         }
16344                     }
16345                 }
16346             }
16347
16348             if (this.mode) {
16349                 if (outEvts.length) {
16350                     dc.b4DragOut(e, outEvts);
16351                     dc.onDragOut(e, outEvts);
16352                 }
16353
16354                 if (enterEvts.length) {
16355                     dc.onDragEnter(e, enterEvts);
16356                 }
16357
16358                 if (overEvts.length) {
16359                     dc.b4DragOver(e, overEvts);
16360                     dc.onDragOver(e, overEvts);
16361                 }
16362
16363                 if (dropEvts.length) {
16364                     dc.b4DragDrop(e, dropEvts);
16365                     dc.onDragDrop(e, dropEvts);
16366                 }
16367
16368             } else {
16369                 // fire dragout events
16370                 var len = 0;
16371                 for (i=0, len=outEvts.length; i<len; ++i) {
16372                     dc.b4DragOut(e, outEvts[i].id);
16373                     dc.onDragOut(e, outEvts[i].id);
16374                 }
16375
16376                 // fire enter events
16377                 for (i=0,len=enterEvts.length; i<len; ++i) {
16378                     // dc.b4DragEnter(e, oDD.id);
16379                     dc.onDragEnter(e, enterEvts[i].id);
16380                 }
16381
16382                 // fire over events
16383                 for (i=0,len=overEvts.length; i<len; ++i) {
16384                     dc.b4DragOver(e, overEvts[i].id);
16385                     dc.onDragOver(e, overEvts[i].id);
16386                 }
16387
16388                 // fire drop events
16389                 for (i=0, len=dropEvts.length; i<len; ++i) {
16390                     dc.b4DragDrop(e, dropEvts[i].id);
16391                     dc.onDragDrop(e, dropEvts[i].id);
16392                 }
16393
16394             }
16395
16396             // notify about a drop that did not find a target
16397             if (isDrop && !dropEvts.length) {
16398                 dc.onInvalidDrop(e);
16399             }
16400
16401         },
16402
16403         /**
16404          * Helper function for getting the best match from the list of drag
16405          * and drop objects returned by the drag and drop events when we are
16406          * in INTERSECT mode.  It returns either the first object that the
16407          * cursor is over, or the object that has the greatest overlap with
16408          * the dragged element.
16409          * @method getBestMatch
16410          * @param  {DragDrop[]} dds The array of drag and drop objects
16411          * targeted
16412          * @return {DragDrop}       The best single match
16413          * @static
16414          */
16415         getBestMatch: function(dds) {
16416             var winner = null;
16417             // Return null if the input is not what we expect
16418             //if (!dds || !dds.length || dds.length == 0) {
16419                // winner = null;
16420             // If there is only one item, it wins
16421             //} else if (dds.length == 1) {
16422
16423             var len = dds.length;
16424
16425             if (len == 1) {
16426                 winner = dds[0];
16427             } else {
16428                 // Loop through the targeted items
16429                 for (var i=0; i<len; ++i) {
16430                     var dd = dds[i];
16431                     // If the cursor is over the object, it wins.  If the
16432                     // cursor is over multiple matches, the first one we come
16433                     // to wins.
16434                     if (dd.cursorIsOver) {
16435                         winner = dd;
16436                         break;
16437                     // Otherwise the object with the most overlap wins
16438                     } else {
16439                         if (!winner ||
16440                             winner.overlap.getArea() < dd.overlap.getArea()) {
16441                             winner = dd;
16442                         }
16443                     }
16444                 }
16445             }
16446
16447             return winner;
16448         },
16449
16450         /**
16451          * Refreshes the cache of the top-left and bottom-right points of the
16452          * drag and drop objects in the specified group(s).  This is in the
16453          * format that is stored in the drag and drop instance, so typical
16454          * usage is:
16455          * <code>
16456          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16457          * </code>
16458          * Alternatively:
16459          * <code>
16460          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16461          * </code>
16462          * @TODO this really should be an indexed array.  Alternatively this
16463          * method could accept both.
16464          * @method refreshCache
16465          * @param {Object} groups an associative array of groups to refresh
16466          * @static
16467          */
16468         refreshCache: function(groups) {
16469             for (var sGroup in groups) {
16470                 if ("string" != typeof sGroup) {
16471                     continue;
16472                 }
16473                 for (var i in this.ids[sGroup]) {
16474                     var oDD = this.ids[sGroup][i];
16475
16476                     if (this.isTypeOfDD(oDD)) {
16477                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16478                         var loc = this.getLocation(oDD);
16479                         if (loc) {
16480                             this.locationCache[oDD.id] = loc;
16481                         } else {
16482                             delete this.locationCache[oDD.id];
16483                             // this will unregister the drag and drop object if
16484                             // the element is not in a usable state
16485                             // oDD.unreg();
16486                         }
16487                     }
16488                 }
16489             }
16490         },
16491
16492         /**
16493          * This checks to make sure an element exists and is in the DOM.  The
16494          * main purpose is to handle cases where innerHTML is used to remove
16495          * drag and drop objects from the DOM.  IE provides an 'unspecified
16496          * error' when trying to access the offsetParent of such an element
16497          * @method verifyEl
16498          * @param {HTMLElement} el the element to check
16499          * @return {boolean} true if the element looks usable
16500          * @static
16501          */
16502         verifyEl: function(el) {
16503             if (el) {
16504                 var parent;
16505                 if(Roo.isIE){
16506                     try{
16507                         parent = el.offsetParent;
16508                     }catch(e){}
16509                 }else{
16510                     parent = el.offsetParent;
16511                 }
16512                 if (parent) {
16513                     return true;
16514                 }
16515             }
16516
16517             return false;
16518         },
16519
16520         /**
16521          * Returns a Region object containing the drag and drop element's position
16522          * and size, including the padding configured for it
16523          * @method getLocation
16524          * @param {DragDrop} oDD the drag and drop object to get the
16525          *                       location for
16526          * @return {Roo.lib.Region} a Region object representing the total area
16527          *                             the element occupies, including any padding
16528          *                             the instance is configured for.
16529          * @static
16530          */
16531         getLocation: function(oDD) {
16532             if (! this.isTypeOfDD(oDD)) {
16533                 return null;
16534             }
16535
16536             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16537
16538             try {
16539                 pos= Roo.lib.Dom.getXY(el);
16540             } catch (e) { }
16541
16542             if (!pos) {
16543                 return null;
16544             }
16545
16546             x1 = pos[0];
16547             x2 = x1 + el.offsetWidth;
16548             y1 = pos[1];
16549             y2 = y1 + el.offsetHeight;
16550
16551             t = y1 - oDD.padding[0];
16552             r = x2 + oDD.padding[1];
16553             b = y2 + oDD.padding[2];
16554             l = x1 - oDD.padding[3];
16555
16556             return new Roo.lib.Region( t, r, b, l );
16557         },
16558
16559         /**
16560          * Checks the cursor location to see if it over the target
16561          * @method isOverTarget
16562          * @param {Roo.lib.Point} pt The point to evaluate
16563          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16564          * @return {boolean} true if the mouse is over the target
16565          * @private
16566          * @static
16567          */
16568         isOverTarget: function(pt, oTarget, intersect) {
16569             // use cache if available
16570             var loc = this.locationCache[oTarget.id];
16571             if (!loc || !this.useCache) {
16572                 loc = this.getLocation(oTarget);
16573                 this.locationCache[oTarget.id] = loc;
16574
16575             }
16576
16577             if (!loc) {
16578                 return false;
16579             }
16580
16581             oTarget.cursorIsOver = loc.contains( pt );
16582
16583             // DragDrop is using this as a sanity check for the initial mousedown
16584             // in this case we are done.  In POINT mode, if the drag obj has no
16585             // contraints, we are also done. Otherwise we need to evaluate the
16586             // location of the target as related to the actual location of the
16587             // dragged element.
16588             var dc = this.dragCurrent;
16589             if (!dc || !dc.getTargetCoord ||
16590                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16591                 return oTarget.cursorIsOver;
16592             }
16593
16594             oTarget.overlap = null;
16595
16596             // Get the current location of the drag element, this is the
16597             // location of the mouse event less the delta that represents
16598             // where the original mousedown happened on the element.  We
16599             // need to consider constraints and ticks as well.
16600             var pos = dc.getTargetCoord(pt.x, pt.y);
16601
16602             var el = dc.getDragEl();
16603             var curRegion = new Roo.lib.Region( pos.y,
16604                                                    pos.x + el.offsetWidth,
16605                                                    pos.y + el.offsetHeight,
16606                                                    pos.x );
16607
16608             var overlap = curRegion.intersect(loc);
16609
16610             if (overlap) {
16611                 oTarget.overlap = overlap;
16612                 return (intersect) ? true : oTarget.cursorIsOver;
16613             } else {
16614                 return false;
16615             }
16616         },
16617
16618         /**
16619          * unload event handler
16620          * @method _onUnload
16621          * @private
16622          * @static
16623          */
16624         _onUnload: function(e, me) {
16625             Roo.dd.DragDropMgr.unregAll();
16626         },
16627
16628         /**
16629          * Cleans up the drag and drop events and objects.
16630          * @method unregAll
16631          * @private
16632          * @static
16633          */
16634         unregAll: function() {
16635
16636             if (this.dragCurrent) {
16637                 this.stopDrag();
16638                 this.dragCurrent = null;
16639             }
16640
16641             this._execOnAll("unreg", []);
16642
16643             for (i in this.elementCache) {
16644                 delete this.elementCache[i];
16645             }
16646
16647             this.elementCache = {};
16648             this.ids = {};
16649         },
16650
16651         /**
16652          * A cache of DOM elements
16653          * @property elementCache
16654          * @private
16655          * @static
16656          */
16657         elementCache: {},
16658
16659         /**
16660          * Get the wrapper for the DOM element specified
16661          * @method getElWrapper
16662          * @param {String} id the id of the element to get
16663          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16664          * @private
16665          * @deprecated This wrapper isn't that useful
16666          * @static
16667          */
16668         getElWrapper: function(id) {
16669             var oWrapper = this.elementCache[id];
16670             if (!oWrapper || !oWrapper.el) {
16671                 oWrapper = this.elementCache[id] =
16672                     new this.ElementWrapper(Roo.getDom(id));
16673             }
16674             return oWrapper;
16675         },
16676
16677         /**
16678          * Returns the actual DOM element
16679          * @method getElement
16680          * @param {String} id the id of the elment to get
16681          * @return {Object} The element
16682          * @deprecated use Roo.getDom instead
16683          * @static
16684          */
16685         getElement: function(id) {
16686             return Roo.getDom(id);
16687         },
16688
16689         /**
16690          * Returns the style property for the DOM element (i.e.,
16691          * document.getElById(id).style)
16692          * @method getCss
16693          * @param {String} id the id of the elment to get
16694          * @return {Object} The style property of the element
16695          * @deprecated use Roo.getDom instead
16696          * @static
16697          */
16698         getCss: function(id) {
16699             var el = Roo.getDom(id);
16700             return (el) ? el.style : null;
16701         },
16702
16703         /**
16704          * Inner class for cached elements
16705          * @class DragDropMgr.ElementWrapper
16706          * @for DragDropMgr
16707          * @private
16708          * @deprecated
16709          */
16710         ElementWrapper: function(el) {
16711                 /**
16712                  * The element
16713                  * @property el
16714                  */
16715                 this.el = el || null;
16716                 /**
16717                  * The element id
16718                  * @property id
16719                  */
16720                 this.id = this.el && el.id;
16721                 /**
16722                  * A reference to the style property
16723                  * @property css
16724                  */
16725                 this.css = this.el && el.style;
16726             },
16727
16728         /**
16729          * Returns the X position of an html element
16730          * @method getPosX
16731          * @param el the element for which to get the position
16732          * @return {int} the X coordinate
16733          * @for DragDropMgr
16734          * @deprecated use Roo.lib.Dom.getX instead
16735          * @static
16736          */
16737         getPosX: function(el) {
16738             return Roo.lib.Dom.getX(el);
16739         },
16740
16741         /**
16742          * Returns the Y position of an html element
16743          * @method getPosY
16744          * @param el the element for which to get the position
16745          * @return {int} the Y coordinate
16746          * @deprecated use Roo.lib.Dom.getY instead
16747          * @static
16748          */
16749         getPosY: function(el) {
16750             return Roo.lib.Dom.getY(el);
16751         },
16752
16753         /**
16754          * Swap two nodes.  In IE, we use the native method, for others we
16755          * emulate the IE behavior
16756          * @method swapNode
16757          * @param n1 the first node to swap
16758          * @param n2 the other node to swap
16759          * @static
16760          */
16761         swapNode: function(n1, n2) {
16762             if (n1.swapNode) {
16763                 n1.swapNode(n2);
16764             } else {
16765                 var p = n2.parentNode;
16766                 var s = n2.nextSibling;
16767
16768                 if (s == n1) {
16769                     p.insertBefore(n1, n2);
16770                 } else if (n2 == n1.nextSibling) {
16771                     p.insertBefore(n2, n1);
16772                 } else {
16773                     n1.parentNode.replaceChild(n2, n1);
16774                     p.insertBefore(n1, s);
16775                 }
16776             }
16777         },
16778
16779         /**
16780          * Returns the current scroll position
16781          * @method getScroll
16782          * @private
16783          * @static
16784          */
16785         getScroll: function () {
16786             var t, l, dde=document.documentElement, db=document.body;
16787             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16788                 t = dde.scrollTop;
16789                 l = dde.scrollLeft;
16790             } else if (db) {
16791                 t = db.scrollTop;
16792                 l = db.scrollLeft;
16793             } else {
16794
16795             }
16796             return { top: t, left: l };
16797         },
16798
16799         /**
16800          * Returns the specified element style property
16801          * @method getStyle
16802          * @param {HTMLElement} el          the element
16803          * @param {string}      styleProp   the style property
16804          * @return {string} The value of the style property
16805          * @deprecated use Roo.lib.Dom.getStyle
16806          * @static
16807          */
16808         getStyle: function(el, styleProp) {
16809             return Roo.fly(el).getStyle(styleProp);
16810         },
16811
16812         /**
16813          * Gets the scrollTop
16814          * @method getScrollTop
16815          * @return {int} the document's scrollTop
16816          * @static
16817          */
16818         getScrollTop: function () { return this.getScroll().top; },
16819
16820         /**
16821          * Gets the scrollLeft
16822          * @method getScrollLeft
16823          * @return {int} the document's scrollTop
16824          * @static
16825          */
16826         getScrollLeft: function () { return this.getScroll().left; },
16827
16828         /**
16829          * Sets the x/y position of an element to the location of the
16830          * target element.
16831          * @method moveToEl
16832          * @param {HTMLElement} moveEl      The element to move
16833          * @param {HTMLElement} targetEl    The position reference element
16834          * @static
16835          */
16836         moveToEl: function (moveEl, targetEl) {
16837             var aCoord = Roo.lib.Dom.getXY(targetEl);
16838             Roo.lib.Dom.setXY(moveEl, aCoord);
16839         },
16840
16841         /**
16842          * Numeric array sort function
16843          * @method numericSort
16844          * @static
16845          */
16846         numericSort: function(a, b) { return (a - b); },
16847
16848         /**
16849          * Internal counter
16850          * @property _timeoutCount
16851          * @private
16852          * @static
16853          */
16854         _timeoutCount: 0,
16855
16856         /**
16857          * Trying to make the load order less important.  Without this we get
16858          * an error if this file is loaded before the Event Utility.
16859          * @method _addListeners
16860          * @private
16861          * @static
16862          */
16863         _addListeners: function() {
16864             var DDM = Roo.dd.DDM;
16865             if ( Roo.lib.Event && document ) {
16866                 DDM._onLoad();
16867             } else {
16868                 if (DDM._timeoutCount > 2000) {
16869                 } else {
16870                     setTimeout(DDM._addListeners, 10);
16871                     if (document && document.body) {
16872                         DDM._timeoutCount += 1;
16873                     }
16874                 }
16875             }
16876         },
16877
16878         /**
16879          * Recursively searches the immediate parent and all child nodes for
16880          * the handle element in order to determine wheter or not it was
16881          * clicked.
16882          * @method handleWasClicked
16883          * @param node the html element to inspect
16884          * @static
16885          */
16886         handleWasClicked: function(node, id) {
16887             if (this.isHandle(id, node.id)) {
16888                 return true;
16889             } else {
16890                 // check to see if this is a text node child of the one we want
16891                 var p = node.parentNode;
16892
16893                 while (p) {
16894                     if (this.isHandle(id, p.id)) {
16895                         return true;
16896                     } else {
16897                         p = p.parentNode;
16898                     }
16899                 }
16900             }
16901
16902             return false;
16903         }
16904
16905     };
16906
16907 }();
16908
16909 // shorter alias, save a few bytes
16910 Roo.dd.DDM = Roo.dd.DragDropMgr;
16911 Roo.dd.DDM._addListeners();
16912
16913 }/*
16914  * Based on:
16915  * Ext JS Library 1.1.1
16916  * Copyright(c) 2006-2007, Ext JS, LLC.
16917  *
16918  * Originally Released Under LGPL - original licence link has changed is not relivant.
16919  *
16920  * Fork - LGPL
16921  * <script type="text/javascript">
16922  */
16923
16924 /**
16925  * @class Roo.dd.DD
16926  * A DragDrop implementation where the linked element follows the
16927  * mouse cursor during a drag.
16928  * @extends Roo.dd.DragDrop
16929  * @constructor
16930  * @param {String} id the id of the linked element
16931  * @param {String} sGroup the group of related DragDrop items
16932  * @param {object} config an object containing configurable attributes
16933  *                Valid properties for DD:
16934  *                    scroll
16935  */
16936 Roo.dd.DD = function(id, sGroup, config) {
16937     if (id) {
16938         this.init(id, sGroup, config);
16939     }
16940 };
16941
16942 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16943
16944     /**
16945      * When set to true, the utility automatically tries to scroll the browser
16946      * window wehn a drag and drop element is dragged near the viewport boundary.
16947      * Defaults to true.
16948      * @property scroll
16949      * @type boolean
16950      */
16951     scroll: true,
16952
16953     /**
16954      * Sets the pointer offset to the distance between the linked element's top
16955      * left corner and the location the element was clicked
16956      * @method autoOffset
16957      * @param {int} iPageX the X coordinate of the click
16958      * @param {int} iPageY the Y coordinate of the click
16959      */
16960     autoOffset: function(iPageX, iPageY) {
16961         var x = iPageX - this.startPageX;
16962         var y = iPageY - this.startPageY;
16963         this.setDelta(x, y);
16964     },
16965
16966     /**
16967      * Sets the pointer offset.  You can call this directly to force the
16968      * offset to be in a particular location (e.g., pass in 0,0 to set it
16969      * to the center of the object)
16970      * @method setDelta
16971      * @param {int} iDeltaX the distance from the left
16972      * @param {int} iDeltaY the distance from the top
16973      */
16974     setDelta: function(iDeltaX, iDeltaY) {
16975         this.deltaX = iDeltaX;
16976         this.deltaY = iDeltaY;
16977     },
16978
16979     /**
16980      * Sets the drag element to the location of the mousedown or click event,
16981      * maintaining the cursor location relative to the location on the element
16982      * that was clicked.  Override this if you want to place the element in a
16983      * location other than where the cursor is.
16984      * @method setDragElPos
16985      * @param {int} iPageX the X coordinate of the mousedown or drag event
16986      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16987      */
16988     setDragElPos: function(iPageX, iPageY) {
16989         // the first time we do this, we are going to check to make sure
16990         // the element has css positioning
16991
16992         var el = this.getDragEl();
16993         this.alignElWithMouse(el, iPageX, iPageY);
16994     },
16995
16996     /**
16997      * Sets the element to the location of the mousedown or click event,
16998      * maintaining the cursor location relative to the location on the element
16999      * that was clicked.  Override this if you want to place the element in a
17000      * location other than where the cursor is.
17001      * @method alignElWithMouse
17002      * @param {HTMLElement} el the element to move
17003      * @param {int} iPageX the X coordinate of the mousedown or drag event
17004      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17005      */
17006     alignElWithMouse: function(el, iPageX, iPageY) {
17007         var oCoord = this.getTargetCoord(iPageX, iPageY);
17008         var fly = el.dom ? el : Roo.fly(el);
17009         if (!this.deltaSetXY) {
17010             var aCoord = [oCoord.x, oCoord.y];
17011             fly.setXY(aCoord);
17012             var newLeft = fly.getLeft(true);
17013             var newTop  = fly.getTop(true);
17014             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17015         } else {
17016             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17017         }
17018
17019         this.cachePosition(oCoord.x, oCoord.y);
17020         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17021         return oCoord;
17022     },
17023
17024     /**
17025      * Saves the most recent position so that we can reset the constraints and
17026      * tick marks on-demand.  We need to know this so that we can calculate the
17027      * number of pixels the element is offset from its original position.
17028      * @method cachePosition
17029      * @param iPageX the current x position (optional, this just makes it so we
17030      * don't have to look it up again)
17031      * @param iPageY the current y position (optional, this just makes it so we
17032      * don't have to look it up again)
17033      */
17034     cachePosition: function(iPageX, iPageY) {
17035         if (iPageX) {
17036             this.lastPageX = iPageX;
17037             this.lastPageY = iPageY;
17038         } else {
17039             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17040             this.lastPageX = aCoord[0];
17041             this.lastPageY = aCoord[1];
17042         }
17043     },
17044
17045     /**
17046      * Auto-scroll the window if the dragged object has been moved beyond the
17047      * visible window boundary.
17048      * @method autoScroll
17049      * @param {int} x the drag element's x position
17050      * @param {int} y the drag element's y position
17051      * @param {int} h the height of the drag element
17052      * @param {int} w the width of the drag element
17053      * @private
17054      */
17055     autoScroll: function(x, y, h, w) {
17056
17057         if (this.scroll) {
17058             // The client height
17059             var clientH = Roo.lib.Dom.getViewWidth();
17060
17061             // The client width
17062             var clientW = Roo.lib.Dom.getViewHeight();
17063
17064             // The amt scrolled down
17065             var st = this.DDM.getScrollTop();
17066
17067             // The amt scrolled right
17068             var sl = this.DDM.getScrollLeft();
17069
17070             // Location of the bottom of the element
17071             var bot = h + y;
17072
17073             // Location of the right of the element
17074             var right = w + x;
17075
17076             // The distance from the cursor to the bottom of the visible area,
17077             // adjusted so that we don't scroll if the cursor is beyond the
17078             // element drag constraints
17079             var toBot = (clientH + st - y - this.deltaY);
17080
17081             // The distance from the cursor to the right of the visible area
17082             var toRight = (clientW + sl - x - this.deltaX);
17083
17084
17085             // How close to the edge the cursor must be before we scroll
17086             // var thresh = (document.all) ? 100 : 40;
17087             var thresh = 40;
17088
17089             // How many pixels to scroll per autoscroll op.  This helps to reduce
17090             // clunky scrolling. IE is more sensitive about this ... it needs this
17091             // value to be higher.
17092             var scrAmt = (document.all) ? 80 : 30;
17093
17094             // Scroll down if we are near the bottom of the visible page and the
17095             // obj extends below the crease
17096             if ( bot > clientH && toBot < thresh ) {
17097                 window.scrollTo(sl, st + scrAmt);
17098             }
17099
17100             // Scroll up if the window is scrolled down and the top of the object
17101             // goes above the top border
17102             if ( y < st && st > 0 && y - st < thresh ) {
17103                 window.scrollTo(sl, st - scrAmt);
17104             }
17105
17106             // Scroll right if the obj is beyond the right border and the cursor is
17107             // near the border.
17108             if ( right > clientW && toRight < thresh ) {
17109                 window.scrollTo(sl + scrAmt, st);
17110             }
17111
17112             // Scroll left if the window has been scrolled to the right and the obj
17113             // extends past the left border
17114             if ( x < sl && sl > 0 && x - sl < thresh ) {
17115                 window.scrollTo(sl - scrAmt, st);
17116             }
17117         }
17118     },
17119
17120     /**
17121      * Finds the location the element should be placed if we want to move
17122      * it to where the mouse location less the click offset would place us.
17123      * @method getTargetCoord
17124      * @param {int} iPageX the X coordinate of the click
17125      * @param {int} iPageY the Y coordinate of the click
17126      * @return an object that contains the coordinates (Object.x and Object.y)
17127      * @private
17128      */
17129     getTargetCoord: function(iPageX, iPageY) {
17130
17131
17132         var x = iPageX - this.deltaX;
17133         var y = iPageY - this.deltaY;
17134
17135         if (this.constrainX) {
17136             if (x < this.minX) { x = this.minX; }
17137             if (x > this.maxX) { x = this.maxX; }
17138         }
17139
17140         if (this.constrainY) {
17141             if (y < this.minY) { y = this.minY; }
17142             if (y > this.maxY) { y = this.maxY; }
17143         }
17144
17145         x = this.getTick(x, this.xTicks);
17146         y = this.getTick(y, this.yTicks);
17147
17148
17149         return {x:x, y:y};
17150     },
17151
17152     /*
17153      * Sets up config options specific to this class. Overrides
17154      * Roo.dd.DragDrop, but all versions of this method through the
17155      * inheritance chain are called
17156      */
17157     applyConfig: function() {
17158         Roo.dd.DD.superclass.applyConfig.call(this);
17159         this.scroll = (this.config.scroll !== false);
17160     },
17161
17162     /*
17163      * Event that fires prior to the onMouseDown event.  Overrides
17164      * Roo.dd.DragDrop.
17165      */
17166     b4MouseDown: function(e) {
17167         // this.resetConstraints();
17168         this.autoOffset(e.getPageX(),
17169                             e.getPageY());
17170     },
17171
17172     /*
17173      * Event that fires prior to the onDrag event.  Overrides
17174      * Roo.dd.DragDrop.
17175      */
17176     b4Drag: function(e) {
17177         this.setDragElPos(e.getPageX(),
17178                             e.getPageY());
17179     },
17180
17181     toString: function() {
17182         return ("DD " + this.id);
17183     }
17184
17185     //////////////////////////////////////////////////////////////////////////
17186     // Debugging ygDragDrop events that can be overridden
17187     //////////////////////////////////////////////////////////////////////////
17188     /*
17189     startDrag: function(x, y) {
17190     },
17191
17192     onDrag: function(e) {
17193     },
17194
17195     onDragEnter: function(e, id) {
17196     },
17197
17198     onDragOver: function(e, id) {
17199     },
17200
17201     onDragOut: function(e, id) {
17202     },
17203
17204     onDragDrop: function(e, id) {
17205     },
17206
17207     endDrag: function(e) {
17208     }
17209
17210     */
17211
17212 });/*
17213  * Based on:
17214  * Ext JS Library 1.1.1
17215  * Copyright(c) 2006-2007, Ext JS, LLC.
17216  *
17217  * Originally Released Under LGPL - original licence link has changed is not relivant.
17218  *
17219  * Fork - LGPL
17220  * <script type="text/javascript">
17221  */
17222
17223 /**
17224  * @class Roo.dd.DDProxy
17225  * A DragDrop implementation that inserts an empty, bordered div into
17226  * the document that follows the cursor during drag operations.  At the time of
17227  * the click, the frame div is resized to the dimensions of the linked html
17228  * element, and moved to the exact location of the linked element.
17229  *
17230  * References to the "frame" element refer to the single proxy element that
17231  * was created to be dragged in place of all DDProxy elements on the
17232  * page.
17233  *
17234  * @extends Roo.dd.DD
17235  * @constructor
17236  * @param {String} id the id of the linked html element
17237  * @param {String} sGroup the group of related DragDrop objects
17238  * @param {object} config an object containing configurable attributes
17239  *                Valid properties for DDProxy in addition to those in DragDrop:
17240  *                   resizeFrame, centerFrame, dragElId
17241  */
17242 Roo.dd.DDProxy = function(id, sGroup, config) {
17243     if (id) {
17244         this.init(id, sGroup, config);
17245         this.initFrame();
17246     }
17247 };
17248
17249 /**
17250  * The default drag frame div id
17251  * @property Roo.dd.DDProxy.dragElId
17252  * @type String
17253  * @static
17254  */
17255 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17256
17257 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17258
17259     /**
17260      * By default we resize the drag frame to be the same size as the element
17261      * we want to drag (this is to get the frame effect).  We can turn it off
17262      * if we want a different behavior.
17263      * @property resizeFrame
17264      * @type boolean
17265      */
17266     resizeFrame: true,
17267
17268     /**
17269      * By default the frame is positioned exactly where the drag element is, so
17270      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17271      * you do not have constraints on the obj is to have the drag frame centered
17272      * around the cursor.  Set centerFrame to true for this effect.
17273      * @property centerFrame
17274      * @type boolean
17275      */
17276     centerFrame: false,
17277
17278     /**
17279      * Creates the proxy element if it does not yet exist
17280      * @method createFrame
17281      */
17282     createFrame: function() {
17283         var self = this;
17284         var body = document.body;
17285
17286         if (!body || !body.firstChild) {
17287             setTimeout( function() { self.createFrame(); }, 50 );
17288             return;
17289         }
17290
17291         var div = this.getDragEl();
17292
17293         if (!div) {
17294             div    = document.createElement("div");
17295             div.id = this.dragElId;
17296             var s  = div.style;
17297
17298             s.position   = "absolute";
17299             s.visibility = "hidden";
17300             s.cursor     = "move";
17301             s.border     = "2px solid #aaa";
17302             s.zIndex     = 999;
17303
17304             // appendChild can blow up IE if invoked prior to the window load event
17305             // while rendering a table.  It is possible there are other scenarios
17306             // that would cause this to happen as well.
17307             body.insertBefore(div, body.firstChild);
17308         }
17309     },
17310
17311     /**
17312      * Initialization for the drag frame element.  Must be called in the
17313      * constructor of all subclasses
17314      * @method initFrame
17315      */
17316     initFrame: function() {
17317         this.createFrame();
17318     },
17319
17320     applyConfig: function() {
17321         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17322
17323         this.resizeFrame = (this.config.resizeFrame !== false);
17324         this.centerFrame = (this.config.centerFrame);
17325         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17326     },
17327
17328     /**
17329      * Resizes the drag frame to the dimensions of the clicked object, positions
17330      * it over the object, and finally displays it
17331      * @method showFrame
17332      * @param {int} iPageX X click position
17333      * @param {int} iPageY Y click position
17334      * @private
17335      */
17336     showFrame: function(iPageX, iPageY) {
17337         var el = this.getEl();
17338         var dragEl = this.getDragEl();
17339         var s = dragEl.style;
17340
17341         this._resizeProxy();
17342
17343         if (this.centerFrame) {
17344             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17345                            Math.round(parseInt(s.height, 10)/2) );
17346         }
17347
17348         this.setDragElPos(iPageX, iPageY);
17349
17350         Roo.fly(dragEl).show();
17351     },
17352
17353     /**
17354      * The proxy is automatically resized to the dimensions of the linked
17355      * element when a drag is initiated, unless resizeFrame is set to false
17356      * @method _resizeProxy
17357      * @private
17358      */
17359     _resizeProxy: function() {
17360         if (this.resizeFrame) {
17361             var el = this.getEl();
17362             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17363         }
17364     },
17365
17366     // overrides Roo.dd.DragDrop
17367     b4MouseDown: function(e) {
17368         var x = e.getPageX();
17369         var y = e.getPageY();
17370         this.autoOffset(x, y);
17371         this.setDragElPos(x, y);
17372     },
17373
17374     // overrides Roo.dd.DragDrop
17375     b4StartDrag: function(x, y) {
17376         // show the drag frame
17377         this.showFrame(x, y);
17378     },
17379
17380     // overrides Roo.dd.DragDrop
17381     b4EndDrag: function(e) {
17382         Roo.fly(this.getDragEl()).hide();
17383     },
17384
17385     // overrides Roo.dd.DragDrop
17386     // By default we try to move the element to the last location of the frame.
17387     // This is so that the default behavior mirrors that of Roo.dd.DD.
17388     endDrag: function(e) {
17389
17390         var lel = this.getEl();
17391         var del = this.getDragEl();
17392
17393         // Show the drag frame briefly so we can get its position
17394         del.style.visibility = "";
17395
17396         this.beforeMove();
17397         // Hide the linked element before the move to get around a Safari
17398         // rendering bug.
17399         lel.style.visibility = "hidden";
17400         Roo.dd.DDM.moveToEl(lel, del);
17401         del.style.visibility = "hidden";
17402         lel.style.visibility = "";
17403
17404         this.afterDrag();
17405     },
17406
17407     beforeMove : function(){
17408
17409     },
17410
17411     afterDrag : function(){
17412
17413     },
17414
17415     toString: function() {
17416         return ("DDProxy " + this.id);
17417     }
17418
17419 });
17420 /*
17421  * Based on:
17422  * Ext JS Library 1.1.1
17423  * Copyright(c) 2006-2007, Ext JS, LLC.
17424  *
17425  * Originally Released Under LGPL - original licence link has changed is not relivant.
17426  *
17427  * Fork - LGPL
17428  * <script type="text/javascript">
17429  */
17430
17431  /**
17432  * @class Roo.dd.DDTarget
17433  * A DragDrop implementation that does not move, but can be a drop
17434  * target.  You would get the same result by simply omitting implementation
17435  * for the event callbacks, but this way we reduce the processing cost of the
17436  * event listener and the callbacks.
17437  * @extends Roo.dd.DragDrop
17438  * @constructor
17439  * @param {String} id the id of the element that is a drop target
17440  * @param {String} sGroup the group of related DragDrop objects
17441  * @param {object} config an object containing configurable attributes
17442  *                 Valid properties for DDTarget in addition to those in
17443  *                 DragDrop:
17444  *                    none
17445  */
17446 Roo.dd.DDTarget = function(id, sGroup, config) {
17447     if (id) {
17448         this.initTarget(id, sGroup, config);
17449     }
17450     if (config.listeners || config.events) { 
17451        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17452             listeners : config.listeners || {}, 
17453             events : config.events || {} 
17454         });    
17455     }
17456 };
17457
17458 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17459 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17460     toString: function() {
17461         return ("DDTarget " + this.id);
17462     }
17463 });
17464 /*
17465  * Based on:
17466  * Ext JS Library 1.1.1
17467  * Copyright(c) 2006-2007, Ext JS, LLC.
17468  *
17469  * Originally Released Under LGPL - original licence link has changed is not relivant.
17470  *
17471  * Fork - LGPL
17472  * <script type="text/javascript">
17473  */
17474  
17475
17476 /**
17477  * @class Roo.dd.ScrollManager
17478  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17479  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17480  * @singleton
17481  */
17482 Roo.dd.ScrollManager = function(){
17483     var ddm = Roo.dd.DragDropMgr;
17484     var els = {};
17485     var dragEl = null;
17486     var proc = {};
17487     
17488     var onStop = function(e){
17489         dragEl = null;
17490         clearProc();
17491     };
17492     
17493     var triggerRefresh = function(){
17494         if(ddm.dragCurrent){
17495              ddm.refreshCache(ddm.dragCurrent.groups);
17496         }
17497     };
17498     
17499     var doScroll = function(){
17500         if(ddm.dragCurrent){
17501             var dds = Roo.dd.ScrollManager;
17502             if(!dds.animate){
17503                 if(proc.el.scroll(proc.dir, dds.increment)){
17504                     triggerRefresh();
17505                 }
17506             }else{
17507                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17508             }
17509         }
17510     };
17511     
17512     var clearProc = function(){
17513         if(proc.id){
17514             clearInterval(proc.id);
17515         }
17516         proc.id = 0;
17517         proc.el = null;
17518         proc.dir = "";
17519     };
17520     
17521     var startProc = function(el, dir){
17522         clearProc();
17523         proc.el = el;
17524         proc.dir = dir;
17525         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17526     };
17527     
17528     var onFire = function(e, isDrop){
17529         if(isDrop || !ddm.dragCurrent){ return; }
17530         var dds = Roo.dd.ScrollManager;
17531         if(!dragEl || dragEl != ddm.dragCurrent){
17532             dragEl = ddm.dragCurrent;
17533             // refresh regions on drag start
17534             dds.refreshCache();
17535         }
17536         
17537         var xy = Roo.lib.Event.getXY(e);
17538         var pt = new Roo.lib.Point(xy[0], xy[1]);
17539         for(var id in els){
17540             var el = els[id], r = el._region;
17541             if(r && r.contains(pt) && el.isScrollable()){
17542                 if(r.bottom - pt.y <= dds.thresh){
17543                     if(proc.el != el){
17544                         startProc(el, "down");
17545                     }
17546                     return;
17547                 }else if(r.right - pt.x <= dds.thresh){
17548                     if(proc.el != el){
17549                         startProc(el, "left");
17550                     }
17551                     return;
17552                 }else if(pt.y - r.top <= dds.thresh){
17553                     if(proc.el != el){
17554                         startProc(el, "up");
17555                     }
17556                     return;
17557                 }else if(pt.x - r.left <= dds.thresh){
17558                     if(proc.el != el){
17559                         startProc(el, "right");
17560                     }
17561                     return;
17562                 }
17563             }
17564         }
17565         clearProc();
17566     };
17567     
17568     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17569     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17570     
17571     return {
17572         /**
17573          * Registers new overflow element(s) to auto scroll
17574          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17575          */
17576         register : function(el){
17577             if(el instanceof Array){
17578                 for(var i = 0, len = el.length; i < len; i++) {
17579                         this.register(el[i]);
17580                 }
17581             }else{
17582                 el = Roo.get(el);
17583                 els[el.id] = el;
17584             }
17585         },
17586         
17587         /**
17588          * Unregisters overflow element(s) so they are no longer scrolled
17589          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17590          */
17591         unregister : function(el){
17592             if(el instanceof Array){
17593                 for(var i = 0, len = el.length; i < len; i++) {
17594                         this.unregister(el[i]);
17595                 }
17596             }else{
17597                 el = Roo.get(el);
17598                 delete els[el.id];
17599             }
17600         },
17601         
17602         /**
17603          * The number of pixels from the edge of a container the pointer needs to be to 
17604          * trigger scrolling (defaults to 25)
17605          * @type Number
17606          */
17607         thresh : 25,
17608         
17609         /**
17610          * The number of pixels to scroll in each scroll increment (defaults to 50)
17611          * @type Number
17612          */
17613         increment : 100,
17614         
17615         /**
17616          * The frequency of scrolls in milliseconds (defaults to 500)
17617          * @type Number
17618          */
17619         frequency : 500,
17620         
17621         /**
17622          * True to animate the scroll (defaults to true)
17623          * @type Boolean
17624          */
17625         animate: true,
17626         
17627         /**
17628          * The animation duration in seconds - 
17629          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17630          * @type Number
17631          */
17632         animDuration: .4,
17633         
17634         /**
17635          * Manually trigger a cache refresh.
17636          */
17637         refreshCache : function(){
17638             for(var id in els){
17639                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17640                     els[id]._region = els[id].getRegion();
17641                 }
17642             }
17643         }
17644     };
17645 }();/*
17646  * Based on:
17647  * Ext JS Library 1.1.1
17648  * Copyright(c) 2006-2007, Ext JS, LLC.
17649  *
17650  * Originally Released Under LGPL - original licence link has changed is not relivant.
17651  *
17652  * Fork - LGPL
17653  * <script type="text/javascript">
17654  */
17655  
17656
17657 /**
17658  * @class Roo.dd.Registry
17659  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17660  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17661  * @singleton
17662  */
17663 Roo.dd.Registry = function(){
17664     var elements = {}; 
17665     var handles = {}; 
17666     var autoIdSeed = 0;
17667
17668     var getId = function(el, autogen){
17669         if(typeof el == "string"){
17670             return el;
17671         }
17672         var id = el.id;
17673         if(!id && autogen !== false){
17674             id = "roodd-" + (++autoIdSeed);
17675             el.id = id;
17676         }
17677         return id;
17678     };
17679     
17680     return {
17681     /**
17682      * Register a drag drop element
17683      * @param {String|HTMLElement} element The id or DOM node to register
17684      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17685      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17686      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17687      * populated in the data object (if applicable):
17688      * <pre>
17689 Value      Description<br />
17690 ---------  ------------------------------------------<br />
17691 handles    Array of DOM nodes that trigger dragging<br />
17692            for the element being registered<br />
17693 isHandle   True if the element passed in triggers<br />
17694            dragging itself, else false
17695 </pre>
17696      */
17697         register : function(el, data){
17698             data = data || {};
17699             if(typeof el == "string"){
17700                 el = document.getElementById(el);
17701             }
17702             data.ddel = el;
17703             elements[getId(el)] = data;
17704             if(data.isHandle !== false){
17705                 handles[data.ddel.id] = data;
17706             }
17707             if(data.handles){
17708                 var hs = data.handles;
17709                 for(var i = 0, len = hs.length; i < len; i++){
17710                         handles[getId(hs[i])] = data;
17711                 }
17712             }
17713         },
17714
17715     /**
17716      * Unregister a drag drop element
17717      * @param {String|HTMLElement}  element The id or DOM node to unregister
17718      */
17719         unregister : function(el){
17720             var id = getId(el, false);
17721             var data = elements[id];
17722             if(data){
17723                 delete elements[id];
17724                 if(data.handles){
17725                     var hs = data.handles;
17726                     for(var i = 0, len = hs.length; i < len; i++){
17727                         delete handles[getId(hs[i], false)];
17728                     }
17729                 }
17730             }
17731         },
17732
17733     /**
17734      * Returns the handle registered for a DOM Node by id
17735      * @param {String|HTMLElement} id The DOM node or id to look up
17736      * @return {Object} handle The custom handle data
17737      */
17738         getHandle : function(id){
17739             if(typeof id != "string"){ // must be element?
17740                 id = id.id;
17741             }
17742             return handles[id];
17743         },
17744
17745     /**
17746      * Returns the handle that is registered for the DOM node that is the target of the event
17747      * @param {Event} e The event
17748      * @return {Object} handle The custom handle data
17749      */
17750         getHandleFromEvent : function(e){
17751             var t = Roo.lib.Event.getTarget(e);
17752             return t ? handles[t.id] : null;
17753         },
17754
17755     /**
17756      * Returns a custom data object that is registered for a DOM node by id
17757      * @param {String|HTMLElement} id The DOM node or id to look up
17758      * @return {Object} data The custom data
17759      */
17760         getTarget : function(id){
17761             if(typeof id != "string"){ // must be element?
17762                 id = id.id;
17763             }
17764             return elements[id];
17765         },
17766
17767     /**
17768      * Returns a custom data object that is registered for the DOM node that is the target of the event
17769      * @param {Event} e The event
17770      * @return {Object} data The custom data
17771      */
17772         getTargetFromEvent : function(e){
17773             var t = Roo.lib.Event.getTarget(e);
17774             return t ? elements[t.id] || handles[t.id] : null;
17775         }
17776     };
17777 }();/*
17778  * Based on:
17779  * Ext JS Library 1.1.1
17780  * Copyright(c) 2006-2007, Ext JS, LLC.
17781  *
17782  * Originally Released Under LGPL - original licence link has changed is not relivant.
17783  *
17784  * Fork - LGPL
17785  * <script type="text/javascript">
17786  */
17787  
17788
17789 /**
17790  * @class Roo.dd.StatusProxy
17791  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17792  * default drag proxy used by all Roo.dd components.
17793  * @constructor
17794  * @param {Object} config
17795  */
17796 Roo.dd.StatusProxy = function(config){
17797     Roo.apply(this, config);
17798     this.id = this.id || Roo.id();
17799     this.el = new Roo.Layer({
17800         dh: {
17801             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17802                 {tag: "div", cls: "x-dd-drop-icon"},
17803                 {tag: "div", cls: "x-dd-drag-ghost"}
17804             ]
17805         }, 
17806         shadow: !config || config.shadow !== false
17807     });
17808     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17809     this.dropStatus = this.dropNotAllowed;
17810 };
17811
17812 Roo.dd.StatusProxy.prototype = {
17813     /**
17814      * @cfg {String} dropAllowed
17815      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17816      */
17817     dropAllowed : "x-dd-drop-ok",
17818     /**
17819      * @cfg {String} dropNotAllowed
17820      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17821      */
17822     dropNotAllowed : "x-dd-drop-nodrop",
17823
17824     /**
17825      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17826      * over the current target element.
17827      * @param {String} cssClass The css class for the new drop status indicator image
17828      */
17829     setStatus : function(cssClass){
17830         cssClass = cssClass || this.dropNotAllowed;
17831         if(this.dropStatus != cssClass){
17832             this.el.replaceClass(this.dropStatus, cssClass);
17833             this.dropStatus = cssClass;
17834         }
17835     },
17836
17837     /**
17838      * Resets the status indicator to the default dropNotAllowed value
17839      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17840      */
17841     reset : function(clearGhost){
17842         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17843         this.dropStatus = this.dropNotAllowed;
17844         if(clearGhost){
17845             this.ghost.update("");
17846         }
17847     },
17848
17849     /**
17850      * Updates the contents of the ghost element
17851      * @param {String} html The html that will replace the current innerHTML of the ghost element
17852      */
17853     update : function(html){
17854         if(typeof html == "string"){
17855             this.ghost.update(html);
17856         }else{
17857             this.ghost.update("");
17858             html.style.margin = "0";
17859             this.ghost.dom.appendChild(html);
17860         }
17861         // ensure float = none set?? cant remember why though.
17862         var el = this.ghost.dom.firstChild;
17863                 if(el){
17864                         Roo.fly(el).setStyle('float', 'none');
17865                 }
17866     },
17867     
17868     /**
17869      * Returns the underlying proxy {@link Roo.Layer}
17870      * @return {Roo.Layer} el
17871     */
17872     getEl : function(){
17873         return this.el;
17874     },
17875
17876     /**
17877      * Returns the ghost element
17878      * @return {Roo.Element} el
17879      */
17880     getGhost : function(){
17881         return this.ghost;
17882     },
17883
17884     /**
17885      * Hides the proxy
17886      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17887      */
17888     hide : function(clear){
17889         this.el.hide();
17890         if(clear){
17891             this.reset(true);
17892         }
17893     },
17894
17895     /**
17896      * Stops the repair animation if it's currently running
17897      */
17898     stop : function(){
17899         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17900             this.anim.stop();
17901         }
17902     },
17903
17904     /**
17905      * Displays this proxy
17906      */
17907     show : function(){
17908         this.el.show();
17909     },
17910
17911     /**
17912      * Force the Layer to sync its shadow and shim positions to the element
17913      */
17914     sync : function(){
17915         this.el.sync();
17916     },
17917
17918     /**
17919      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17920      * invalid drop operation by the item being dragged.
17921      * @param {Array} xy The XY position of the element ([x, y])
17922      * @param {Function} callback The function to call after the repair is complete
17923      * @param {Object} scope The scope in which to execute the callback
17924      */
17925     repair : function(xy, callback, scope){
17926         this.callback = callback;
17927         this.scope = scope;
17928         if(xy && this.animRepair !== false){
17929             this.el.addClass("x-dd-drag-repair");
17930             this.el.hideUnders(true);
17931             this.anim = this.el.shift({
17932                 duration: this.repairDuration || .5,
17933                 easing: 'easeOut',
17934                 xy: xy,
17935                 stopFx: true,
17936                 callback: this.afterRepair,
17937                 scope: this
17938             });
17939         }else{
17940             this.afterRepair();
17941         }
17942     },
17943
17944     // private
17945     afterRepair : function(){
17946         this.hide(true);
17947         if(typeof this.callback == "function"){
17948             this.callback.call(this.scope || this);
17949         }
17950         this.callback = null;
17951         this.scope = null;
17952     }
17953 };/*
17954  * Based on:
17955  * Ext JS Library 1.1.1
17956  * Copyright(c) 2006-2007, Ext JS, LLC.
17957  *
17958  * Originally Released Under LGPL - original licence link has changed is not relivant.
17959  *
17960  * Fork - LGPL
17961  * <script type="text/javascript">
17962  */
17963
17964 /**
17965  * @class Roo.dd.DragSource
17966  * @extends Roo.dd.DDProxy
17967  * A simple class that provides the basic implementation needed to make any element draggable.
17968  * @constructor
17969  * @param {String/HTMLElement/Element} el The container element
17970  * @param {Object} config
17971  */
17972 Roo.dd.DragSource = function(el, config){
17973     this.el = Roo.get(el);
17974     this.dragData = {};
17975     
17976     Roo.apply(this, config);
17977     
17978     if(!this.proxy){
17979         this.proxy = new Roo.dd.StatusProxy();
17980     }
17981
17982     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17983           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17984     
17985     this.dragging = false;
17986 };
17987
17988 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
17989     /**
17990      * @cfg {String} dropAllowed
17991      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
17992      */
17993     dropAllowed : "x-dd-drop-ok",
17994     /**
17995      * @cfg {String} dropNotAllowed
17996      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
17997      */
17998     dropNotAllowed : "x-dd-drop-nodrop",
17999
18000     /**
18001      * Returns the data object associated with this drag source
18002      * @return {Object} data An object containing arbitrary data
18003      */
18004     getDragData : function(e){
18005         return this.dragData;
18006     },
18007
18008     // private
18009     onDragEnter : function(e, id){
18010         var target = Roo.dd.DragDropMgr.getDDById(id);
18011         this.cachedTarget = target;
18012         if(this.beforeDragEnter(target, e, id) !== false){
18013             if(target.isNotifyTarget){
18014                 var status = target.notifyEnter(this, e, this.dragData);
18015                 this.proxy.setStatus(status);
18016             }else{
18017                 this.proxy.setStatus(this.dropAllowed);
18018             }
18019             
18020             if(this.afterDragEnter){
18021                 /**
18022                  * An empty function by default, but provided so that you can perform a custom action
18023                  * when the dragged item enters the drop target by providing an implementation.
18024                  * @param {Roo.dd.DragDrop} target The drop target
18025                  * @param {Event} e The event object
18026                  * @param {String} id The id of the dragged element
18027                  * @method afterDragEnter
18028                  */
18029                 this.afterDragEnter(target, e, id);
18030             }
18031         }
18032     },
18033
18034     /**
18035      * An empty function by default, but provided so that you can perform a custom action
18036      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18037      * @param {Roo.dd.DragDrop} target The drop target
18038      * @param {Event} e The event object
18039      * @param {String} id The id of the dragged element
18040      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18041      */
18042     beforeDragEnter : function(target, e, id){
18043         return true;
18044     },
18045
18046     // private
18047     alignElWithMouse: function() {
18048         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18049         this.proxy.sync();
18050     },
18051
18052     // private
18053     onDragOver : function(e, id){
18054         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18055         if(this.beforeDragOver(target, e, id) !== false){
18056             if(target.isNotifyTarget){
18057                 var status = target.notifyOver(this, e, this.dragData);
18058                 this.proxy.setStatus(status);
18059             }
18060
18061             if(this.afterDragOver){
18062                 /**
18063                  * An empty function by default, but provided so that you can perform a custom action
18064                  * while the dragged item is over the drop target by providing an implementation.
18065                  * @param {Roo.dd.DragDrop} target The drop target
18066                  * @param {Event} e The event object
18067                  * @param {String} id The id of the dragged element
18068                  * @method afterDragOver
18069                  */
18070                 this.afterDragOver(target, e, id);
18071             }
18072         }
18073     },
18074
18075     /**
18076      * An empty function by default, but provided so that you can perform a custom action
18077      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18078      * @param {Roo.dd.DragDrop} target The drop target
18079      * @param {Event} e The event object
18080      * @param {String} id The id of the dragged element
18081      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18082      */
18083     beforeDragOver : function(target, e, id){
18084         return true;
18085     },
18086
18087     // private
18088     onDragOut : function(e, id){
18089         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18090         if(this.beforeDragOut(target, e, id) !== false){
18091             if(target.isNotifyTarget){
18092                 target.notifyOut(this, e, this.dragData);
18093             }
18094             this.proxy.reset();
18095             if(this.afterDragOut){
18096                 /**
18097                  * An empty function by default, but provided so that you can perform a custom action
18098                  * after the dragged item is dragged out of the target without dropping.
18099                  * @param {Roo.dd.DragDrop} target The drop target
18100                  * @param {Event} e The event object
18101                  * @param {String} id The id of the dragged element
18102                  * @method afterDragOut
18103                  */
18104                 this.afterDragOut(target, e, id);
18105             }
18106         }
18107         this.cachedTarget = null;
18108     },
18109
18110     /**
18111      * An empty function by default, but provided so that you can perform a custom action before the dragged
18112      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18113      * @param {Roo.dd.DragDrop} target The drop target
18114      * @param {Event} e The event object
18115      * @param {String} id The id of the dragged element
18116      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18117      */
18118     beforeDragOut : function(target, e, id){
18119         return true;
18120     },
18121     
18122     // private
18123     onDragDrop : function(e, id){
18124         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18125         if(this.beforeDragDrop(target, e, id) !== false){
18126             if(target.isNotifyTarget){
18127                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18128                     this.onValidDrop(target, e, id);
18129                 }else{
18130                     this.onInvalidDrop(target, e, id);
18131                 }
18132             }else{
18133                 this.onValidDrop(target, e, id);
18134             }
18135             
18136             if(this.afterDragDrop){
18137                 /**
18138                  * An empty function by default, but provided so that you can perform a custom action
18139                  * after a valid drag drop has occurred by providing an implementation.
18140                  * @param {Roo.dd.DragDrop} target The drop target
18141                  * @param {Event} e The event object
18142                  * @param {String} id The id of the dropped element
18143                  * @method afterDragDrop
18144                  */
18145                 this.afterDragDrop(target, e, id);
18146             }
18147         }
18148         delete this.cachedTarget;
18149     },
18150
18151     /**
18152      * An empty function by default, but provided so that you can perform a custom action before the dragged
18153      * item is dropped onto the target and optionally cancel the onDragDrop.
18154      * @param {Roo.dd.DragDrop} target The drop target
18155      * @param {Event} e The event object
18156      * @param {String} id The id of the dragged element
18157      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18158      */
18159     beforeDragDrop : function(target, e, id){
18160         return true;
18161     },
18162
18163     // private
18164     onValidDrop : function(target, e, id){
18165         this.hideProxy();
18166         if(this.afterValidDrop){
18167             /**
18168              * An empty function by default, but provided so that you can perform a custom action
18169              * after a valid drop has occurred by providing an implementation.
18170              * @param {Object} target The target DD 
18171              * @param {Event} e The event object
18172              * @param {String} id The id of the dropped element
18173              * @method afterInvalidDrop
18174              */
18175             this.afterValidDrop(target, e, id);
18176         }
18177     },
18178
18179     // private
18180     getRepairXY : function(e, data){
18181         return this.el.getXY();  
18182     },
18183
18184     // private
18185     onInvalidDrop : function(target, e, id){
18186         this.beforeInvalidDrop(target, e, id);
18187         if(this.cachedTarget){
18188             if(this.cachedTarget.isNotifyTarget){
18189                 this.cachedTarget.notifyOut(this, e, this.dragData);
18190             }
18191             this.cacheTarget = null;
18192         }
18193         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18194
18195         if(this.afterInvalidDrop){
18196             /**
18197              * An empty function by default, but provided so that you can perform a custom action
18198              * after an invalid drop has occurred by providing an implementation.
18199              * @param {Event} e The event object
18200              * @param {String} id The id of the dropped element
18201              * @method afterInvalidDrop
18202              */
18203             this.afterInvalidDrop(e, id);
18204         }
18205     },
18206
18207     // private
18208     afterRepair : function(){
18209         if(Roo.enableFx){
18210             this.el.highlight(this.hlColor || "c3daf9");
18211         }
18212         this.dragging = false;
18213     },
18214
18215     /**
18216      * An empty function by default, but provided so that you can perform a custom action after an invalid
18217      * drop has occurred.
18218      * @param {Roo.dd.DragDrop} target The drop target
18219      * @param {Event} e The event object
18220      * @param {String} id The id of the dragged element
18221      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18222      */
18223     beforeInvalidDrop : function(target, e, id){
18224         return true;
18225     },
18226
18227     // private
18228     handleMouseDown : function(e){
18229         if(this.dragging) {
18230             return;
18231         }
18232         var data = this.getDragData(e);
18233         if(data && this.onBeforeDrag(data, e) !== false){
18234             this.dragData = data;
18235             this.proxy.stop();
18236             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18237         } 
18238     },
18239
18240     /**
18241      * An empty function by default, but provided so that you can perform a custom action before the initial
18242      * drag event begins and optionally cancel it.
18243      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18244      * @param {Event} e The event object
18245      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18246      */
18247     onBeforeDrag : function(data, e){
18248         return true;
18249     },
18250
18251     /**
18252      * An empty function by default, but provided so that you can perform a custom action once the initial
18253      * drag event has begun.  The drag cannot be canceled from this function.
18254      * @param {Number} x The x position of the click on the dragged object
18255      * @param {Number} y The y position of the click on the dragged object
18256      */
18257     onStartDrag : Roo.emptyFn,
18258
18259     // private - YUI override
18260     startDrag : function(x, y){
18261         this.proxy.reset();
18262         this.dragging = true;
18263         this.proxy.update("");
18264         this.onInitDrag(x, y);
18265         this.proxy.show();
18266     },
18267
18268     // private
18269     onInitDrag : function(x, y){
18270         var clone = this.el.dom.cloneNode(true);
18271         clone.id = Roo.id(); // prevent duplicate ids
18272         this.proxy.update(clone);
18273         this.onStartDrag(x, y);
18274         return true;
18275     },
18276
18277     /**
18278      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18279      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18280      */
18281     getProxy : function(){
18282         return this.proxy;  
18283     },
18284
18285     /**
18286      * Hides the drag source's {@link Roo.dd.StatusProxy}
18287      */
18288     hideProxy : function(){
18289         this.proxy.hide();  
18290         this.proxy.reset(true);
18291         this.dragging = false;
18292     },
18293
18294     // private
18295     triggerCacheRefresh : function(){
18296         Roo.dd.DDM.refreshCache(this.groups);
18297     },
18298
18299     // private - override to prevent hiding
18300     b4EndDrag: function(e) {
18301     },
18302
18303     // private - override to prevent moving
18304     endDrag : function(e){
18305         this.onEndDrag(this.dragData, e);
18306     },
18307
18308     // private
18309     onEndDrag : function(data, e){
18310     },
18311     
18312     // private - pin to cursor
18313     autoOffset : function(x, y) {
18314         this.setDelta(-12, -20);
18315     }    
18316 });/*
18317  * Based on:
18318  * Ext JS Library 1.1.1
18319  * Copyright(c) 2006-2007, Ext JS, LLC.
18320  *
18321  * Originally Released Under LGPL - original licence link has changed is not relivant.
18322  *
18323  * Fork - LGPL
18324  * <script type="text/javascript">
18325  */
18326
18327
18328 /**
18329  * @class Roo.dd.DropTarget
18330  * @extends Roo.dd.DDTarget
18331  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18332  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18333  * @constructor
18334  * @param {String/HTMLElement/Element} el The container element
18335  * @param {Object} config
18336  */
18337 Roo.dd.DropTarget = function(el, config){
18338     this.el = Roo.get(el);
18339     
18340     var listeners = false; ;
18341     if (config && config.listeners) {
18342         listeners= config.listeners;
18343         delete config.listeners;
18344     }
18345     Roo.apply(this, config);
18346     
18347     if(this.containerScroll){
18348         Roo.dd.ScrollManager.register(this.el);
18349     }
18350     this.addEvents( {
18351          /**
18352          * @scope Roo.dd.DropTarget
18353          */
18354          
18355          /**
18356          * @event enter
18357          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18358          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18359          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18360          * 
18361          * IMPORTANT : it should set this.overClass and this.dropAllowed
18362          * 
18363          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18364          * @param {Event} e The event
18365          * @param {Object} data An object containing arbitrary data supplied by the drag source
18366          */
18367         "enter" : true,
18368         
18369          /**
18370          * @event over
18371          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18372          * This method will be called on every mouse movement while the drag source is over the drop target.
18373          * This default implementation simply returns the dropAllowed config value.
18374          * 
18375          * IMPORTANT : it should set this.dropAllowed
18376          * 
18377          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18378          * @param {Event} e The event
18379          * @param {Object} data An object containing arbitrary data supplied by the drag source
18380          
18381          */
18382         "over" : true,
18383         /**
18384          * @event out
18385          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18386          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18387          * overClass (if any) from the drop element.
18388          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18389          * @param {Event} e The event
18390          * @param {Object} data An object containing arbitrary data supplied by the drag source
18391          */
18392          "out" : true,
18393          
18394         /**
18395          * @event drop
18396          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18397          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18398          * implementation that does something to process the drop event and returns true so that the drag source's
18399          * repair action does not run.
18400          * 
18401          * IMPORTANT : it should set this.success
18402          * 
18403          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18404          * @param {Event} e The event
18405          * @param {Object} data An object containing arbitrary data supplied by the drag source
18406         */
18407          "drop" : true
18408     });
18409             
18410      
18411     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18412         this.el.dom, 
18413         this.ddGroup || this.group,
18414         {
18415             isTarget: true,
18416             listeners : listeners || {} 
18417            
18418         
18419         }
18420     );
18421
18422 };
18423
18424 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18425     /**
18426      * @cfg {String} overClass
18427      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18428      */
18429      /**
18430      * @cfg {String} ddGroup
18431      * The drag drop group to handle drop events for
18432      */
18433      
18434     /**
18435      * @cfg {String} dropAllowed
18436      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18437      */
18438     dropAllowed : "x-dd-drop-ok",
18439     /**
18440      * @cfg {String} dropNotAllowed
18441      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18442      */
18443     dropNotAllowed : "x-dd-drop-nodrop",
18444     /**
18445      * @cfg {boolean} success
18446      * set this after drop listener.. 
18447      */
18448     success : false,
18449     /**
18450      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18451      * if the drop point is valid for over/enter..
18452      */
18453     valid : false,
18454     // private
18455     isTarget : true,
18456
18457     // private
18458     isNotifyTarget : true,
18459     
18460     /**
18461      * @hide
18462      */
18463     notifyEnter : function(dd, e, data)
18464     {
18465         this.valid = true;
18466         this.fireEvent('enter', dd, e, data);
18467         if(this.overClass){
18468             this.el.addClass(this.overClass);
18469         }
18470         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18471             this.valid ? this.dropAllowed : this.dropNotAllowed
18472         );
18473     },
18474
18475     /**
18476      * @hide
18477      */
18478     notifyOver : function(dd, e, data)
18479     {
18480         this.valid = true;
18481         this.fireEvent('over', dd, e, data);
18482         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18483             this.valid ? this.dropAllowed : this.dropNotAllowed
18484         );
18485     },
18486
18487     /**
18488      * @hide
18489      */
18490     notifyOut : function(dd, e, data)
18491     {
18492         this.fireEvent('out', dd, e, data);
18493         if(this.overClass){
18494             this.el.removeClass(this.overClass);
18495         }
18496     },
18497
18498     /**
18499      * @hide
18500      */
18501     notifyDrop : function(dd, e, data)
18502     {
18503         this.success = false;
18504         this.fireEvent('drop', dd, e, data);
18505         return this.success;
18506     }
18507 });/*
18508  * Based on:
18509  * Ext JS Library 1.1.1
18510  * Copyright(c) 2006-2007, Ext JS, LLC.
18511  *
18512  * Originally Released Under LGPL - original licence link has changed is not relivant.
18513  *
18514  * Fork - LGPL
18515  * <script type="text/javascript">
18516  */
18517
18518
18519 /**
18520  * @class Roo.dd.DragZone
18521  * @extends Roo.dd.DragSource
18522  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18523  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18524  * @constructor
18525  * @param {String/HTMLElement/Element} el The container element
18526  * @param {Object} config
18527  */
18528 Roo.dd.DragZone = function(el, config){
18529     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18530     if(this.containerScroll){
18531         Roo.dd.ScrollManager.register(this.el);
18532     }
18533 };
18534
18535 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18536     /**
18537      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18538      * for auto scrolling during drag operations.
18539      */
18540     /**
18541      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18542      * method after a failed drop (defaults to "c3daf9" - light blue)
18543      */
18544
18545     /**
18546      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18547      * for a valid target to drag based on the mouse down. Override this method
18548      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18549      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18550      * @param {EventObject} e The mouse down event
18551      * @return {Object} The dragData
18552      */
18553     getDragData : function(e){
18554         return Roo.dd.Registry.getHandleFromEvent(e);
18555     },
18556     
18557     /**
18558      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18559      * this.dragData.ddel
18560      * @param {Number} x The x position of the click on the dragged object
18561      * @param {Number} y The y position of the click on the dragged object
18562      * @return {Boolean} true to continue the drag, false to cancel
18563      */
18564     onInitDrag : function(x, y){
18565         this.proxy.update(this.dragData.ddel.cloneNode(true));
18566         this.onStartDrag(x, y);
18567         return true;
18568     },
18569     
18570     /**
18571      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18572      */
18573     afterRepair : function(){
18574         if(Roo.enableFx){
18575             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18576         }
18577         this.dragging = false;
18578     },
18579
18580     /**
18581      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18582      * the XY of this.dragData.ddel
18583      * @param {EventObject} e The mouse up event
18584      * @return {Array} The xy location (e.g. [100, 200])
18585      */
18586     getRepairXY : function(e){
18587         return Roo.Element.fly(this.dragData.ddel).getXY();  
18588     }
18589 });/*
18590  * Based on:
18591  * Ext JS Library 1.1.1
18592  * Copyright(c) 2006-2007, Ext JS, LLC.
18593  *
18594  * Originally Released Under LGPL - original licence link has changed is not relivant.
18595  *
18596  * Fork - LGPL
18597  * <script type="text/javascript">
18598  */
18599 /**
18600  * @class Roo.dd.DropZone
18601  * @extends Roo.dd.DropTarget
18602  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18603  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18604  * @constructor
18605  * @param {String/HTMLElement/Element} el The container element
18606  * @param {Object} config
18607  */
18608 Roo.dd.DropZone = function(el, config){
18609     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18610 };
18611
18612 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18613     /**
18614      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18615      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18616      * provide your own custom lookup.
18617      * @param {Event} e The event
18618      * @return {Object} data The custom data
18619      */
18620     getTargetFromEvent : function(e){
18621         return Roo.dd.Registry.getTargetFromEvent(e);
18622     },
18623
18624     /**
18625      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18626      * that it has registered.  This method has no default implementation and should be overridden to provide
18627      * node-specific processing if necessary.
18628      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18629      * {@link #getTargetFromEvent} for this node)
18630      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18631      * @param {Event} e The event
18632      * @param {Object} data An object containing arbitrary data supplied by the drag source
18633      */
18634     onNodeEnter : function(n, dd, e, data){
18635         
18636     },
18637
18638     /**
18639      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18640      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18641      * overridden to provide the proper feedback.
18642      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18643      * {@link #getTargetFromEvent} for this node)
18644      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18645      * @param {Event} e The event
18646      * @param {Object} data An object containing arbitrary data supplied by the drag source
18647      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18648      * underlying {@link Roo.dd.StatusProxy} can be updated
18649      */
18650     onNodeOver : function(n, dd, e, data){
18651         return this.dropAllowed;
18652     },
18653
18654     /**
18655      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18656      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18657      * node-specific processing if necessary.
18658      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18659      * {@link #getTargetFromEvent} for this node)
18660      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18661      * @param {Event} e The event
18662      * @param {Object} data An object containing arbitrary data supplied by the drag source
18663      */
18664     onNodeOut : function(n, dd, e, data){
18665         
18666     },
18667
18668     /**
18669      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18670      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18671      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18672      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18673      * {@link #getTargetFromEvent} for this node)
18674      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18675      * @param {Event} e The event
18676      * @param {Object} data An object containing arbitrary data supplied by the drag source
18677      * @return {Boolean} True if the drop was valid, else false
18678      */
18679     onNodeDrop : function(n, dd, e, data){
18680         return false;
18681     },
18682
18683     /**
18684      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18685      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18686      * it should be overridden to provide the proper feedback if necessary.
18687      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18688      * @param {Event} e The event
18689      * @param {Object} data An object containing arbitrary data supplied by the drag source
18690      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18691      * underlying {@link Roo.dd.StatusProxy} can be updated
18692      */
18693     onContainerOver : function(dd, e, data){
18694         return this.dropNotAllowed;
18695     },
18696
18697     /**
18698      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18699      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18700      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18701      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18702      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18703      * @param {Event} e The event
18704      * @param {Object} data An object containing arbitrary data supplied by the drag source
18705      * @return {Boolean} True if the drop was valid, else false
18706      */
18707     onContainerDrop : function(dd, e, data){
18708         return false;
18709     },
18710
18711     /**
18712      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18713      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18714      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18715      * you should override this method and provide a custom implementation.
18716      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18717      * @param {Event} e The event
18718      * @param {Object} data An object containing arbitrary data supplied by the drag source
18719      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18720      * underlying {@link Roo.dd.StatusProxy} can be updated
18721      */
18722     notifyEnter : function(dd, e, data){
18723         return this.dropNotAllowed;
18724     },
18725
18726     /**
18727      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18728      * This method will be called on every mouse movement while the drag source is over the drop zone.
18729      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18730      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18731      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18732      * registered node, it will call {@link #onContainerOver}.
18733      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18734      * @param {Event} e The event
18735      * @param {Object} data An object containing arbitrary data supplied by the drag source
18736      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18737      * underlying {@link Roo.dd.StatusProxy} can be updated
18738      */
18739     notifyOver : function(dd, e, data){
18740         var n = this.getTargetFromEvent(e);
18741         if(!n){ // not over valid drop target
18742             if(this.lastOverNode){
18743                 this.onNodeOut(this.lastOverNode, dd, e, data);
18744                 this.lastOverNode = null;
18745             }
18746             return this.onContainerOver(dd, e, data);
18747         }
18748         if(this.lastOverNode != n){
18749             if(this.lastOverNode){
18750                 this.onNodeOut(this.lastOverNode, dd, e, data);
18751             }
18752             this.onNodeEnter(n, dd, e, data);
18753             this.lastOverNode = n;
18754         }
18755         return this.onNodeOver(n, dd, e, data);
18756     },
18757
18758     /**
18759      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18760      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18761      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18762      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18763      * @param {Event} e The event
18764      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18765      */
18766     notifyOut : function(dd, e, data){
18767         if(this.lastOverNode){
18768             this.onNodeOut(this.lastOverNode, dd, e, data);
18769             this.lastOverNode = null;
18770         }
18771     },
18772
18773     /**
18774      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18775      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18776      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18777      * otherwise it will call {@link #onContainerDrop}.
18778      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18779      * @param {Event} e The event
18780      * @param {Object} data An object containing arbitrary data supplied by the drag source
18781      * @return {Boolean} True if the drop was valid, else false
18782      */
18783     notifyDrop : function(dd, e, data){
18784         if(this.lastOverNode){
18785             this.onNodeOut(this.lastOverNode, dd, e, data);
18786             this.lastOverNode = null;
18787         }
18788         var n = this.getTargetFromEvent(e);
18789         return n ?
18790             this.onNodeDrop(n, dd, e, data) :
18791             this.onContainerDrop(dd, e, data);
18792     },
18793
18794     // private
18795     triggerCacheRefresh : function(){
18796         Roo.dd.DDM.refreshCache(this.groups);
18797     }  
18798 });/*
18799  * Based on:
18800  * Ext JS Library 1.1.1
18801  * Copyright(c) 2006-2007, Ext JS, LLC.
18802  *
18803  * Originally Released Under LGPL - original licence link has changed is not relivant.
18804  *
18805  * Fork - LGPL
18806  * <script type="text/javascript">
18807  */
18808
18809
18810 /**
18811  * @class Roo.data.SortTypes
18812  * @singleton
18813  * Defines the default sorting (casting?) comparison functions used when sorting data.
18814  */
18815 Roo.data.SortTypes = {
18816     /**
18817      * Default sort that does nothing
18818      * @param {Mixed} s The value being converted
18819      * @return {Mixed} The comparison value
18820      */
18821     none : function(s){
18822         return s;
18823     },
18824     
18825     /**
18826      * The regular expression used to strip tags
18827      * @type {RegExp}
18828      * @property
18829      */
18830     stripTagsRE : /<\/?[^>]+>/gi,
18831     
18832     /**
18833      * Strips all HTML tags to sort on text only
18834      * @param {Mixed} s The value being converted
18835      * @return {String} The comparison value
18836      */
18837     asText : function(s){
18838         return String(s).replace(this.stripTagsRE, "");
18839     },
18840     
18841     /**
18842      * Strips all HTML tags to sort on text only - Case insensitive
18843      * @param {Mixed} s The value being converted
18844      * @return {String} The comparison value
18845      */
18846     asUCText : function(s){
18847         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18848     },
18849     
18850     /**
18851      * Case insensitive string
18852      * @param {Mixed} s The value being converted
18853      * @return {String} The comparison value
18854      */
18855     asUCString : function(s) {
18856         return String(s).toUpperCase();
18857     },
18858     
18859     /**
18860      * Date sorting
18861      * @param {Mixed} s The value being converted
18862      * @return {Number} The comparison value
18863      */
18864     asDate : function(s) {
18865         if(!s){
18866             return 0;
18867         }
18868         if(s instanceof Date){
18869             return s.getTime();
18870         }
18871         return Date.parse(String(s));
18872     },
18873     
18874     /**
18875      * Float sorting
18876      * @param {Mixed} s The value being converted
18877      * @return {Float} The comparison value
18878      */
18879     asFloat : function(s) {
18880         var val = parseFloat(String(s).replace(/,/g, ""));
18881         if(isNaN(val)) val = 0;
18882         return val;
18883     },
18884     
18885     /**
18886      * Integer sorting
18887      * @param {Mixed} s The value being converted
18888      * @return {Number} The comparison value
18889      */
18890     asInt : function(s) {
18891         var val = parseInt(String(s).replace(/,/g, ""));
18892         if(isNaN(val)) val = 0;
18893         return val;
18894     }
18895 };/*
18896  * Based on:
18897  * Ext JS Library 1.1.1
18898  * Copyright(c) 2006-2007, Ext JS, LLC.
18899  *
18900  * Originally Released Under LGPL - original licence link has changed is not relivant.
18901  *
18902  * Fork - LGPL
18903  * <script type="text/javascript">
18904  */
18905
18906 /**
18907 * @class Roo.data.Record
18908  * Instances of this class encapsulate both record <em>definition</em> information, and record
18909  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18910  * to access Records cached in an {@link Roo.data.Store} object.<br>
18911  * <p>
18912  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18913  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18914  * objects.<br>
18915  * <p>
18916  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18917  * @constructor
18918  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18919  * {@link #create}. The parameters are the same.
18920  * @param {Array} data An associative Array of data values keyed by the field name.
18921  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18922  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18923  * not specified an integer id is generated.
18924  */
18925 Roo.data.Record = function(data, id){
18926     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18927     this.data = data;
18928 };
18929
18930 /**
18931  * Generate a constructor for a specific record layout.
18932  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18933  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18934  * Each field definition object may contain the following properties: <ul>
18935  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
18936  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18937  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18938  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18939  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18940  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18941  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18942  * this may be omitted.</p></li>
18943  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18944  * <ul><li>auto (Default, implies no conversion)</li>
18945  * <li>string</li>
18946  * <li>int</li>
18947  * <li>float</li>
18948  * <li>boolean</li>
18949  * <li>date</li></ul></p></li>
18950  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18951  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18952  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18953  * by the Reader into an object that will be stored in the Record. It is passed the
18954  * following parameters:<ul>
18955  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18956  * </ul></p></li>
18957  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18958  * </ul>
18959  * <br>usage:<br><pre><code>
18960 var TopicRecord = Roo.data.Record.create(
18961     {name: 'title', mapping: 'topic_title'},
18962     {name: 'author', mapping: 'username'},
18963     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18964     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18965     {name: 'lastPoster', mapping: 'user2'},
18966     {name: 'excerpt', mapping: 'post_text'}
18967 );
18968
18969 var myNewRecord = new TopicRecord({
18970     title: 'Do my job please',
18971     author: 'noobie',
18972     totalPosts: 1,
18973     lastPost: new Date(),
18974     lastPoster: 'Animal',
18975     excerpt: 'No way dude!'
18976 });
18977 myStore.add(myNewRecord);
18978 </code></pre>
18979  * @method create
18980  * @static
18981  */
18982 Roo.data.Record.create = function(o){
18983     var f = function(){
18984         f.superclass.constructor.apply(this, arguments);
18985     };
18986     Roo.extend(f, Roo.data.Record);
18987     var p = f.prototype;
18988     p.fields = new Roo.util.MixedCollection(false, function(field){
18989         return field.name;
18990     });
18991     for(var i = 0, len = o.length; i < len; i++){
18992         p.fields.add(new Roo.data.Field(o[i]));
18993     }
18994     f.getField = function(name){
18995         return p.fields.get(name);  
18996     };
18997     return f;
18998 };
18999
19000 Roo.data.Record.AUTO_ID = 1000;
19001 Roo.data.Record.EDIT = 'edit';
19002 Roo.data.Record.REJECT = 'reject';
19003 Roo.data.Record.COMMIT = 'commit';
19004
19005 Roo.data.Record.prototype = {
19006     /**
19007      * Readonly flag - true if this record has been modified.
19008      * @type Boolean
19009      */
19010     dirty : false,
19011     editing : false,
19012     error: null,
19013     modified: null,
19014
19015     // private
19016     join : function(store){
19017         this.store = store;
19018     },
19019
19020     /**
19021      * Set the named field to the specified value.
19022      * @param {String} name The name of the field to set.
19023      * @param {Object} value The value to set the field to.
19024      */
19025     set : function(name, value){
19026         if(this.data[name] == value){
19027             return;
19028         }
19029         this.dirty = true;
19030         if(!this.modified){
19031             this.modified = {};
19032         }
19033         if(typeof this.modified[name] == 'undefined'){
19034             this.modified[name] = this.data[name];
19035         }
19036         this.data[name] = value;
19037         if(!this.editing){
19038             this.store.afterEdit(this);
19039         }       
19040     },
19041
19042     /**
19043      * Get the value of the named field.
19044      * @param {String} name The name of the field to get the value of.
19045      * @return {Object} The value of the field.
19046      */
19047     get : function(name){
19048         return this.data[name]; 
19049     },
19050
19051     // private
19052     beginEdit : function(){
19053         this.editing = true;
19054         this.modified = {}; 
19055     },
19056
19057     // private
19058     cancelEdit : function(){
19059         this.editing = false;
19060         delete this.modified;
19061     },
19062
19063     // private
19064     endEdit : function(){
19065         this.editing = false;
19066         if(this.dirty && this.store){
19067             this.store.afterEdit(this);
19068         }
19069     },
19070
19071     /**
19072      * Usually called by the {@link Roo.data.Store} which owns the Record.
19073      * Rejects all changes made to the Record since either creation, or the last commit operation.
19074      * Modified fields are reverted to their original values.
19075      * <p>
19076      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19077      * of reject operations.
19078      */
19079     reject : function(){
19080         var m = this.modified;
19081         for(var n in m){
19082             if(typeof m[n] != "function"){
19083                 this.data[n] = m[n];
19084             }
19085         }
19086         this.dirty = false;
19087         delete this.modified;
19088         this.editing = false;
19089         if(this.store){
19090             this.store.afterReject(this);
19091         }
19092     },
19093
19094     /**
19095      * Usually called by the {@link Roo.data.Store} which owns the Record.
19096      * Commits all changes made to the Record since either creation, or the last commit operation.
19097      * <p>
19098      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19099      * of commit operations.
19100      */
19101     commit : function(){
19102         this.dirty = false;
19103         delete this.modified;
19104         this.editing = false;
19105         if(this.store){
19106             this.store.afterCommit(this);
19107         }
19108     },
19109
19110     // private
19111     hasError : function(){
19112         return this.error != null;
19113     },
19114
19115     // private
19116     clearError : function(){
19117         this.error = null;
19118     },
19119
19120     /**
19121      * Creates a copy of this record.
19122      * @param {String} id (optional) A new record id if you don't want to use this record's id
19123      * @return {Record}
19124      */
19125     copy : function(newId) {
19126         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19127     }
19128 };/*
19129  * Based on:
19130  * Ext JS Library 1.1.1
19131  * Copyright(c) 2006-2007, Ext JS, LLC.
19132  *
19133  * Originally Released Under LGPL - original licence link has changed is not relivant.
19134  *
19135  * Fork - LGPL
19136  * <script type="text/javascript">
19137  */
19138
19139
19140
19141 /**
19142  * @class Roo.data.Store
19143  * @extends Roo.util.Observable
19144  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19145  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19146  * <p>
19147  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
19148  * has no knowledge of the format of the data returned by the Proxy.<br>
19149  * <p>
19150  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19151  * instances from the data object. These records are cached and made available through accessor functions.
19152  * @constructor
19153  * Creates a new Store.
19154  * @param {Object} config A config object containing the objects needed for the Store to access data,
19155  * and read the data into Records.
19156  */
19157 Roo.data.Store = function(config){
19158     this.data = new Roo.util.MixedCollection(false);
19159     this.data.getKey = function(o){
19160         return o.id;
19161     };
19162     this.baseParams = {};
19163     // private
19164     this.paramNames = {
19165         "start" : "start",
19166         "limit" : "limit",
19167         "sort" : "sort",
19168         "dir" : "dir",
19169         "multisort" : "_multisort"
19170     };
19171
19172     if(config && config.data){
19173         this.inlineData = config.data;
19174         delete config.data;
19175     }
19176
19177     Roo.apply(this, config);
19178     
19179     if(this.reader){ // reader passed
19180         this.reader = Roo.factory(this.reader, Roo.data);
19181         this.reader.xmodule = this.xmodule || false;
19182         if(!this.recordType){
19183             this.recordType = this.reader.recordType;
19184         }
19185         if(this.reader.onMetaChange){
19186             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19187         }
19188     }
19189
19190     if(this.recordType){
19191         this.fields = this.recordType.prototype.fields;
19192     }
19193     this.modified = [];
19194
19195     this.addEvents({
19196         /**
19197          * @event datachanged
19198          * Fires when the data cache has changed, and a widget which is using this Store
19199          * as a Record cache should refresh its view.
19200          * @param {Store} this
19201          */
19202         datachanged : true,
19203         /**
19204          * @event metachange
19205          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19206          * @param {Store} this
19207          * @param {Object} meta The JSON metadata
19208          */
19209         metachange : true,
19210         /**
19211          * @event add
19212          * Fires when Records have been added to the Store
19213          * @param {Store} this
19214          * @param {Roo.data.Record[]} records The array of Records added
19215          * @param {Number} index The index at which the record(s) were added
19216          */
19217         add : true,
19218         /**
19219          * @event remove
19220          * Fires when a Record has been removed from the Store
19221          * @param {Store} this
19222          * @param {Roo.data.Record} record The Record that was removed
19223          * @param {Number} index The index at which the record was removed
19224          */
19225         remove : true,
19226         /**
19227          * @event update
19228          * Fires when a Record has been updated
19229          * @param {Store} this
19230          * @param {Roo.data.Record} record The Record that was updated
19231          * @param {String} operation The update operation being performed.  Value may be one of:
19232          * <pre><code>
19233  Roo.data.Record.EDIT
19234  Roo.data.Record.REJECT
19235  Roo.data.Record.COMMIT
19236          * </code></pre>
19237          */
19238         update : true,
19239         /**
19240          * @event clear
19241          * Fires when the data cache has been cleared.
19242          * @param {Store} this
19243          */
19244         clear : true,
19245         /**
19246          * @event beforeload
19247          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19248          * the load action will be canceled.
19249          * @param {Store} this
19250          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19251          */
19252         beforeload : true,
19253         /**
19254          * @event load
19255          * Fires after a new set of Records has been loaded.
19256          * @param {Store} this
19257          * @param {Roo.data.Record[]} records The Records that were loaded
19258          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19259          */
19260         load : true,
19261         /**
19262          * @event loadexception
19263          * Fires if an exception occurs in the Proxy during loading.
19264          * Called with the signature of the Proxy's "loadexception" event.
19265          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19266          * 
19267          * @param {Proxy} 
19268          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19269          * @param {Object} load options 
19270          * @param {Object} jsonData from your request (normally this contains the Exception)
19271          */
19272         loadexception : true
19273     });
19274     
19275     if(this.proxy){
19276         this.proxy = Roo.factory(this.proxy, Roo.data);
19277         this.proxy.xmodule = this.xmodule || false;
19278         this.relayEvents(this.proxy,  ["loadexception"]);
19279     }
19280     this.sortToggle = {};
19281
19282     Roo.data.Store.superclass.constructor.call(this);
19283
19284     if(this.inlineData){
19285         this.loadData(this.inlineData);
19286         delete this.inlineData;
19287     }
19288 };
19289 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19290      /**
19291     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19292     * without a remote query - used by combo/forms at present.
19293     */
19294     
19295     /**
19296     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19297     */
19298     /**
19299     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19300     */
19301     /**
19302     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19303     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19304     */
19305     /**
19306     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19307     * on any HTTP request
19308     */
19309     /**
19310     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19311     */
19312     /**
19313     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns)
19314     */
19315     multiSort: false,
19316     /**
19317     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19318     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19319     */
19320     remoteSort : false,
19321
19322     /**
19323     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19324      * loaded or when a record is removed. (defaults to false).
19325     */
19326     pruneModifiedRecords : false,
19327
19328     // private
19329     lastOptions : null,
19330
19331     /**
19332      * Add Records to the Store and fires the add event.
19333      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19334      */
19335     add : function(records){
19336         records = [].concat(records);
19337         for(var i = 0, len = records.length; i < len; i++){
19338             records[i].join(this);
19339         }
19340         var index = this.data.length;
19341         this.data.addAll(records);
19342         this.fireEvent("add", this, records, index);
19343     },
19344
19345     /**
19346      * Remove a Record from the Store and fires the remove event.
19347      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19348      */
19349     remove : function(record){
19350         var index = this.data.indexOf(record);
19351         this.data.removeAt(index);
19352         if(this.pruneModifiedRecords){
19353             this.modified.remove(record);
19354         }
19355         this.fireEvent("remove", this, record, index);
19356     },
19357
19358     /**
19359      * Remove all Records from the Store and fires the clear event.
19360      */
19361     removeAll : function(){
19362         this.data.clear();
19363         if(this.pruneModifiedRecords){
19364             this.modified = [];
19365         }
19366         this.fireEvent("clear", this);
19367     },
19368
19369     /**
19370      * Inserts Records to the Store at the given index and fires the add event.
19371      * @param {Number} index The start index at which to insert the passed Records.
19372      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19373      */
19374     insert : function(index, records){
19375         records = [].concat(records);
19376         for(var i = 0, len = records.length; i < len; i++){
19377             this.data.insert(index, records[i]);
19378             records[i].join(this);
19379         }
19380         this.fireEvent("add", this, records, index);
19381     },
19382
19383     /**
19384      * Get the index within the cache of the passed Record.
19385      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19386      * @return {Number} The index of the passed Record. Returns -1 if not found.
19387      */
19388     indexOf : function(record){
19389         return this.data.indexOf(record);
19390     },
19391
19392     /**
19393      * Get the index within the cache of the Record with the passed id.
19394      * @param {String} id The id of the Record to find.
19395      * @return {Number} The index of the Record. Returns -1 if not found.
19396      */
19397     indexOfId : function(id){
19398         return this.data.indexOfKey(id);
19399     },
19400
19401     /**
19402      * Get the Record with the specified id.
19403      * @param {String} id The id of the Record to find.
19404      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19405      */
19406     getById : function(id){
19407         return this.data.key(id);
19408     },
19409
19410     /**
19411      * Get the Record at the specified index.
19412      * @param {Number} index The index of the Record to find.
19413      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19414      */
19415     getAt : function(index){
19416         return this.data.itemAt(index);
19417     },
19418
19419     /**
19420      * Returns a range of Records between specified indices.
19421      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19422      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19423      * @return {Roo.data.Record[]} An array of Records
19424      */
19425     getRange : function(start, end){
19426         return this.data.getRange(start, end);
19427     },
19428
19429     // private
19430     storeOptions : function(o){
19431         o = Roo.apply({}, o);
19432         delete o.callback;
19433         delete o.scope;
19434         this.lastOptions = o;
19435     },
19436
19437     /**
19438      * Loads the Record cache from the configured Proxy using the configured Reader.
19439      * <p>
19440      * If using remote paging, then the first load call must specify the <em>start</em>
19441      * and <em>limit</em> properties in the options.params property to establish the initial
19442      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19443      * <p>
19444      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19445      * and this call will return before the new data has been loaded. Perform any post-processing
19446      * in a callback function, or in a "load" event handler.</strong>
19447      * <p>
19448      * @param {Object} options An object containing properties which control loading options:<ul>
19449      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19450      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19451      * passed the following arguments:<ul>
19452      * <li>r : Roo.data.Record[]</li>
19453      * <li>options: Options object from the load call</li>
19454      * <li>success: Boolean success indicator</li></ul></li>
19455      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19456      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19457      * </ul>
19458      */
19459     load : function(options){
19460         options = options || {};
19461         if(this.fireEvent("beforeload", this, options) !== false){
19462             this.storeOptions(options);
19463             var p = Roo.apply(options.params || {}, this.baseParams);
19464             // if meta was not loaded from remote source.. try requesting it.
19465             if (!this.reader.metaFromRemote) {
19466                 p._requestMeta = 1;
19467             }
19468             if(this.sortInfo && this.remoteSort){
19469                 var pn = this.paramNames;
19470                 p[pn["sort"]] = this.sortInfo.field;
19471                 p[pn["dir"]] = this.sortInfo.direction;
19472             }
19473             if (this.multiSort) {
19474                 var pn = this.paramNames;
19475                 p[pn["multisort"]] = Roo.encode(this.sortToggle);
19476             }
19477             
19478             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19479         }
19480     },
19481
19482     /**
19483      * Reloads the Record cache from the configured Proxy using the configured Reader and
19484      * the options from the last load operation performed.
19485      * @param {Object} options (optional) An object containing properties which may override the options
19486      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19487      * the most recently used options are reused).
19488      */
19489     reload : function(options){
19490         this.load(Roo.applyIf(options||{}, this.lastOptions));
19491     },
19492
19493     // private
19494     // Called as a callback by the Reader during a load operation.
19495     loadRecords : function(o, options, success){
19496         if(!o || success === false){
19497             if(success !== false){
19498                 this.fireEvent("load", this, [], options);
19499             }
19500             if(options.callback){
19501                 options.callback.call(options.scope || this, [], options, false);
19502             }
19503             return;
19504         }
19505         // if data returned failure - throw an exception.
19506         if (o.success === false) {
19507             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19508             return;
19509         }
19510         var r = o.records, t = o.totalRecords || r.length;
19511         if(!options || options.add !== true){
19512             if(this.pruneModifiedRecords){
19513                 this.modified = [];
19514             }
19515             for(var i = 0, len = r.length; i < len; i++){
19516                 r[i].join(this);
19517             }
19518             if(this.snapshot){
19519                 this.data = this.snapshot;
19520                 delete this.snapshot;
19521             }
19522             this.data.clear();
19523             this.data.addAll(r);
19524             this.totalLength = t;
19525             this.applySort();
19526             this.fireEvent("datachanged", this);
19527         }else{
19528             this.totalLength = Math.max(t, this.data.length+r.length);
19529             this.add(r);
19530         }
19531         this.fireEvent("load", this, r, options);
19532         if(options.callback){
19533             options.callback.call(options.scope || this, r, options, true);
19534         }
19535     },
19536
19537     /**
19538      * Loads data from a passed data block. A Reader which understands the format of the data
19539      * must have been configured in the constructor.
19540      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19541      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19542      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19543      */
19544     loadData : function(o, append){
19545         var r = this.reader.readRecords(o);
19546         this.loadRecords(r, {add: append}, true);
19547     },
19548
19549     /**
19550      * Gets the number of cached records.
19551      * <p>
19552      * <em>If using paging, this may not be the total size of the dataset. If the data object
19553      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19554      * the data set size</em>
19555      */
19556     getCount : function(){
19557         return this.data.length || 0;
19558     },
19559
19560     /**
19561      * Gets the total number of records in the dataset as returned by the server.
19562      * <p>
19563      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19564      * the dataset size</em>
19565      */
19566     getTotalCount : function(){
19567         return this.totalLength || 0;
19568     },
19569
19570     /**
19571      * Returns the sort state of the Store as an object with two properties:
19572      * <pre><code>
19573  field {String} The name of the field by which the Records are sorted
19574  direction {String} The sort order, "ASC" or "DESC"
19575      * </code></pre>
19576      */
19577     getSortState : function(){
19578         return this.sortInfo;
19579     },
19580
19581     // private
19582     applySort : function(){
19583         if(this.sortInfo && !this.remoteSort){
19584             var s = this.sortInfo, f = s.field;
19585             var st = this.fields.get(f).sortType;
19586             var fn = function(r1, r2){
19587                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19588                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19589             };
19590             this.data.sort(s.direction, fn);
19591             if(this.snapshot && this.snapshot != this.data){
19592                 this.snapshot.sort(s.direction, fn);
19593             }
19594         }
19595     },
19596
19597     /**
19598      * Sets the default sort column and order to be used by the next load operation.
19599      * @param {String} fieldName The name of the field to sort by.
19600      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19601      */
19602     setDefaultSort : function(field, dir){
19603         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19604     },
19605
19606     /**
19607      * Sort the Records.
19608      * If remote sorting is used, the sort is performed on the server, and the cache is
19609      * reloaded. If local sorting is used, the cache is sorted internally.
19610      * @param {String} fieldName The name of the field to sort by.
19611      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19612      */
19613     sort : function(fieldName, dir){
19614         var f = this.fields.get(fieldName);
19615         if(!dir){
19616             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19617             
19618             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19619                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19620             }else{
19621                 dir = f.sortDir;
19622             }
19623         }
19624         this.sortToggle[f.name] = dir;
19625         this.sortInfo = {field: f.name, direction: dir};
19626         if(!this.remoteSort){
19627             this.applySort();
19628             this.fireEvent("datachanged", this);
19629         }else{
19630             this.load(this.lastOptions);
19631         }
19632     },
19633
19634     /**
19635      * Calls the specified function for each of the Records in the cache.
19636      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19637      * Returning <em>false</em> aborts and exits the iteration.
19638      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19639      */
19640     each : function(fn, scope){
19641         this.data.each(fn, scope);
19642     },
19643
19644     /**
19645      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19646      * (e.g., during paging).
19647      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19648      */
19649     getModifiedRecords : function(){
19650         return this.modified;
19651     },
19652
19653     // private
19654     createFilterFn : function(property, value, anyMatch){
19655         if(!value.exec){ // not a regex
19656             value = String(value);
19657             if(value.length == 0){
19658                 return false;
19659             }
19660             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19661         }
19662         return function(r){
19663             return value.test(r.data[property]);
19664         };
19665     },
19666
19667     /**
19668      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19669      * @param {String} property A field on your records
19670      * @param {Number} start The record index to start at (defaults to 0)
19671      * @param {Number} end The last record index to include (defaults to length - 1)
19672      * @return {Number} The sum
19673      */
19674     sum : function(property, start, end){
19675         var rs = this.data.items, v = 0;
19676         start = start || 0;
19677         end = (end || end === 0) ? end : rs.length-1;
19678
19679         for(var i = start; i <= end; i++){
19680             v += (rs[i].data[property] || 0);
19681         }
19682         return v;
19683     },
19684
19685     /**
19686      * Filter the records by a specified property.
19687      * @param {String} field A field on your records
19688      * @param {String/RegExp} value Either a string that the field
19689      * should start with or a RegExp to test against the field
19690      * @param {Boolean} anyMatch True to match any part not just the beginning
19691      */
19692     filter : function(property, value, anyMatch){
19693         var fn = this.createFilterFn(property, value, anyMatch);
19694         return fn ? this.filterBy(fn) : this.clearFilter();
19695     },
19696
19697     /**
19698      * Filter by a function. The specified function will be called with each
19699      * record in this data source. If the function returns true the record is included,
19700      * otherwise it is filtered.
19701      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19702      * @param {Object} scope (optional) The scope of the function (defaults to this)
19703      */
19704     filterBy : function(fn, scope){
19705         this.snapshot = this.snapshot || this.data;
19706         this.data = this.queryBy(fn, scope||this);
19707         this.fireEvent("datachanged", this);
19708     },
19709
19710     /**
19711      * Query the records by a specified property.
19712      * @param {String} field A field on your records
19713      * @param {String/RegExp} value Either a string that the field
19714      * should start with or a RegExp to test against the field
19715      * @param {Boolean} anyMatch True to match any part not just the beginning
19716      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19717      */
19718     query : function(property, value, anyMatch){
19719         var fn = this.createFilterFn(property, value, anyMatch);
19720         return fn ? this.queryBy(fn) : this.data.clone();
19721     },
19722
19723     /**
19724      * Query by a function. The specified function will be called with each
19725      * record in this data source. If the function returns true the record is included
19726      * in the results.
19727      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19728      * @param {Object} scope (optional) The scope of the function (defaults to this)
19729       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19730      **/
19731     queryBy : function(fn, scope){
19732         var data = this.snapshot || this.data;
19733         return data.filterBy(fn, scope||this);
19734     },
19735
19736     /**
19737      * Collects unique values for a particular dataIndex from this store.
19738      * @param {String} dataIndex The property to collect
19739      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19740      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19741      * @return {Array} An array of the unique values
19742      **/
19743     collect : function(dataIndex, allowNull, bypassFilter){
19744         var d = (bypassFilter === true && this.snapshot) ?
19745                 this.snapshot.items : this.data.items;
19746         var v, sv, r = [], l = {};
19747         for(var i = 0, len = d.length; i < len; i++){
19748             v = d[i].data[dataIndex];
19749             sv = String(v);
19750             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19751                 l[sv] = true;
19752                 r[r.length] = v;
19753             }
19754         }
19755         return r;
19756     },
19757
19758     /**
19759      * Revert to a view of the Record cache with no filtering applied.
19760      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19761      */
19762     clearFilter : function(suppressEvent){
19763         if(this.snapshot && this.snapshot != this.data){
19764             this.data = this.snapshot;
19765             delete this.snapshot;
19766             if(suppressEvent !== true){
19767                 this.fireEvent("datachanged", this);
19768             }
19769         }
19770     },
19771
19772     // private
19773     afterEdit : function(record){
19774         if(this.modified.indexOf(record) == -1){
19775             this.modified.push(record);
19776         }
19777         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19778     },
19779
19780     // private
19781     afterReject : function(record){
19782         this.modified.remove(record);
19783         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19784     },
19785
19786     // private
19787     afterCommit : function(record){
19788         this.modified.remove(record);
19789         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19790     },
19791
19792     /**
19793      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19794      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19795      */
19796     commitChanges : function(){
19797         var m = this.modified.slice(0);
19798         this.modified = [];
19799         for(var i = 0, len = m.length; i < len; i++){
19800             m[i].commit();
19801         }
19802     },
19803
19804     /**
19805      * Cancel outstanding changes on all changed records.
19806      */
19807     rejectChanges : function(){
19808         var m = this.modified.slice(0);
19809         this.modified = [];
19810         for(var i = 0, len = m.length; i < len; i++){
19811             m[i].reject();
19812         }
19813     },
19814
19815     onMetaChange : function(meta, rtype, o){
19816         this.recordType = rtype;
19817         this.fields = rtype.prototype.fields;
19818         delete this.snapshot;
19819         this.sortInfo = meta.sortInfo || this.sortInfo;
19820         this.modified = [];
19821         this.fireEvent('metachange', this, this.reader.meta);
19822     }
19823 });/*
19824  * Based on:
19825  * Ext JS Library 1.1.1
19826  * Copyright(c) 2006-2007, Ext JS, LLC.
19827  *
19828  * Originally Released Under LGPL - original licence link has changed is not relivant.
19829  *
19830  * Fork - LGPL
19831  * <script type="text/javascript">
19832  */
19833
19834 /**
19835  * @class Roo.data.SimpleStore
19836  * @extends Roo.data.Store
19837  * Small helper class to make creating Stores from Array data easier.
19838  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19839  * @cfg {Array} fields An array of field definition objects, or field name strings.
19840  * @cfg {Array} data The multi-dimensional array of data
19841  * @constructor
19842  * @param {Object} config
19843  */
19844 Roo.data.SimpleStore = function(config){
19845     Roo.data.SimpleStore.superclass.constructor.call(this, {
19846         isLocal : true,
19847         reader: new Roo.data.ArrayReader({
19848                 id: config.id
19849             },
19850             Roo.data.Record.create(config.fields)
19851         ),
19852         proxy : new Roo.data.MemoryProxy(config.data)
19853     });
19854     this.load();
19855 };
19856 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19857  * Based on:
19858  * Ext JS Library 1.1.1
19859  * Copyright(c) 2006-2007, Ext JS, LLC.
19860  *
19861  * Originally Released Under LGPL - original licence link has changed is not relivant.
19862  *
19863  * Fork - LGPL
19864  * <script type="text/javascript">
19865  */
19866
19867 /**
19868 /**
19869  * @extends Roo.data.Store
19870  * @class Roo.data.JsonStore
19871  * Small helper class to make creating Stores for JSON data easier. <br/>
19872 <pre><code>
19873 var store = new Roo.data.JsonStore({
19874     url: 'get-images.php',
19875     root: 'images',
19876     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19877 });
19878 </code></pre>
19879  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19880  * JsonReader and HttpProxy (unless inline data is provided).</b>
19881  * @cfg {Array} fields An array of field definition objects, or field name strings.
19882  * @constructor
19883  * @param {Object} config
19884  */
19885 Roo.data.JsonStore = function(c){
19886     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19887         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19888         reader: new Roo.data.JsonReader(c, c.fields)
19889     }));
19890 };
19891 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19892  * Based on:
19893  * Ext JS Library 1.1.1
19894  * Copyright(c) 2006-2007, Ext JS, LLC.
19895  *
19896  * Originally Released Under LGPL - original licence link has changed is not relivant.
19897  *
19898  * Fork - LGPL
19899  * <script type="text/javascript">
19900  */
19901
19902  
19903 Roo.data.Field = function(config){
19904     if(typeof config == "string"){
19905         config = {name: config};
19906     }
19907     Roo.apply(this, config);
19908     
19909     if(!this.type){
19910         this.type = "auto";
19911     }
19912     
19913     var st = Roo.data.SortTypes;
19914     // named sortTypes are supported, here we look them up
19915     if(typeof this.sortType == "string"){
19916         this.sortType = st[this.sortType];
19917     }
19918     
19919     // set default sortType for strings and dates
19920     if(!this.sortType){
19921         switch(this.type){
19922             case "string":
19923                 this.sortType = st.asUCString;
19924                 break;
19925             case "date":
19926                 this.sortType = st.asDate;
19927                 break;
19928             default:
19929                 this.sortType = st.none;
19930         }
19931     }
19932
19933     // define once
19934     var stripRe = /[\$,%]/g;
19935
19936     // prebuilt conversion function for this field, instead of
19937     // switching every time we're reading a value
19938     if(!this.convert){
19939         var cv, dateFormat = this.dateFormat;
19940         switch(this.type){
19941             case "":
19942             case "auto":
19943             case undefined:
19944                 cv = function(v){ return v; };
19945                 break;
19946             case "string":
19947                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19948                 break;
19949             case "int":
19950                 cv = function(v){
19951                     return v !== undefined && v !== null && v !== '' ?
19952                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19953                     };
19954                 break;
19955             case "float":
19956                 cv = function(v){
19957                     return v !== undefined && v !== null && v !== '' ?
19958                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19959                     };
19960                 break;
19961             case "bool":
19962             case "boolean":
19963                 cv = function(v){ return v === true || v === "true" || v == 1; };
19964                 break;
19965             case "date":
19966                 cv = function(v){
19967                     if(!v){
19968                         return '';
19969                     }
19970                     if(v instanceof Date){
19971                         return v;
19972                     }
19973                     if(dateFormat){
19974                         if(dateFormat == "timestamp"){
19975                             return new Date(v*1000);
19976                         }
19977                         return Date.parseDate(v, dateFormat);
19978                     }
19979                     var parsed = Date.parse(v);
19980                     return parsed ? new Date(parsed) : null;
19981                 };
19982              break;
19983             
19984         }
19985         this.convert = cv;
19986     }
19987 };
19988
19989 Roo.data.Field.prototype = {
19990     dateFormat: null,
19991     defaultValue: "",
19992     mapping: null,
19993     sortType : null,
19994     sortDir : "ASC"
19995 };/*
19996  * Based on:
19997  * Ext JS Library 1.1.1
19998  * Copyright(c) 2006-2007, Ext JS, LLC.
19999  *
20000  * Originally Released Under LGPL - original licence link has changed is not relivant.
20001  *
20002  * Fork - LGPL
20003  * <script type="text/javascript">
20004  */
20005  
20006 // Base class for reading structured data from a data source.  This class is intended to be
20007 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20008
20009 /**
20010  * @class Roo.data.DataReader
20011  * Base class for reading structured data from a data source.  This class is intended to be
20012  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20013  */
20014
20015 Roo.data.DataReader = function(meta, recordType){
20016     
20017     this.meta = meta;
20018     
20019     this.recordType = recordType instanceof Array ? 
20020         Roo.data.Record.create(recordType) : recordType;
20021 };
20022
20023 Roo.data.DataReader.prototype = {
20024      /**
20025      * Create an empty record
20026      * @param {Object} data (optional) - overlay some values
20027      * @return {Roo.data.Record} record created.
20028      */
20029     newRow :  function(d) {
20030         var da =  {};
20031         this.recordType.prototype.fields.each(function(c) {
20032             switch( c.type) {
20033                 case 'int' : da[c.name] = 0; break;
20034                 case 'date' : da[c.name] = new Date(); break;
20035                 case 'float' : da[c.name] = 0.0; break;
20036                 case 'boolean' : da[c.name] = false; break;
20037                 default : da[c.name] = ""; break;
20038             }
20039             
20040         });
20041         return new this.recordType(Roo.apply(da, d));
20042     }
20043     
20044 };/*
20045  * Based on:
20046  * Ext JS Library 1.1.1
20047  * Copyright(c) 2006-2007, Ext JS, LLC.
20048  *
20049  * Originally Released Under LGPL - original licence link has changed is not relivant.
20050  *
20051  * Fork - LGPL
20052  * <script type="text/javascript">
20053  */
20054
20055 /**
20056  * @class Roo.data.DataProxy
20057  * @extends Roo.data.Observable
20058  * This class is an abstract base class for implementations which provide retrieval of
20059  * unformatted data objects.<br>
20060  * <p>
20061  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20062  * (of the appropriate type which knows how to parse the data object) to provide a block of
20063  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20064  * <p>
20065  * Custom implementations must implement the load method as described in
20066  * {@link Roo.data.HttpProxy#load}.
20067  */
20068 Roo.data.DataProxy = function(){
20069     this.addEvents({
20070         /**
20071          * @event beforeload
20072          * Fires before a network request is made to retrieve a data object.
20073          * @param {Object} This DataProxy object.
20074          * @param {Object} params The params parameter to the load function.
20075          */
20076         beforeload : true,
20077         /**
20078          * @event load
20079          * Fires before the load method's callback is called.
20080          * @param {Object} This DataProxy object.
20081          * @param {Object} o The data object.
20082          * @param {Object} arg The callback argument object passed to the load function.
20083          */
20084         load : true,
20085         /**
20086          * @event loadexception
20087          * Fires if an Exception occurs during data retrieval.
20088          * @param {Object} This DataProxy object.
20089          * @param {Object} o The data object.
20090          * @param {Object} arg The callback argument object passed to the load function.
20091          * @param {Object} e The Exception.
20092          */
20093         loadexception : true
20094     });
20095     Roo.data.DataProxy.superclass.constructor.call(this);
20096 };
20097
20098 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20099
20100     /**
20101      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20102      */
20103 /*
20104  * Based on:
20105  * Ext JS Library 1.1.1
20106  * Copyright(c) 2006-2007, Ext JS, LLC.
20107  *
20108  * Originally Released Under LGPL - original licence link has changed is not relivant.
20109  *
20110  * Fork - LGPL
20111  * <script type="text/javascript">
20112  */
20113 /**
20114  * @class Roo.data.MemoryProxy
20115  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20116  * to the Reader when its load method is called.
20117  * @constructor
20118  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20119  */
20120 Roo.data.MemoryProxy = function(data){
20121     if (data.data) {
20122         data = data.data;
20123     }
20124     Roo.data.MemoryProxy.superclass.constructor.call(this);
20125     this.data = data;
20126 };
20127
20128 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20129     /**
20130      * Load data from the requested source (in this case an in-memory
20131      * data object passed to the constructor), read the data object into
20132      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20133      * process that block using the passed callback.
20134      * @param {Object} params This parameter is not used by the MemoryProxy class.
20135      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20136      * object into a block of Roo.data.Records.
20137      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20138      * The function must be passed <ul>
20139      * <li>The Record block object</li>
20140      * <li>The "arg" argument from the load function</li>
20141      * <li>A boolean success indicator</li>
20142      * </ul>
20143      * @param {Object} scope The scope in which to call the callback
20144      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20145      */
20146     load : function(params, reader, callback, scope, arg){
20147         params = params || {};
20148         var result;
20149         try {
20150             result = reader.readRecords(this.data);
20151         }catch(e){
20152             this.fireEvent("loadexception", this, arg, null, e);
20153             callback.call(scope, null, arg, false);
20154             return;
20155         }
20156         callback.call(scope, result, arg, true);
20157     },
20158     
20159     // private
20160     update : function(params, records){
20161         
20162     }
20163 });/*
20164  * Based on:
20165  * Ext JS Library 1.1.1
20166  * Copyright(c) 2006-2007, Ext JS, LLC.
20167  *
20168  * Originally Released Under LGPL - original licence link has changed is not relivant.
20169  *
20170  * Fork - LGPL
20171  * <script type="text/javascript">
20172  */
20173 /**
20174  * @class Roo.data.HttpProxy
20175  * @extends Roo.data.DataProxy
20176  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20177  * configured to reference a certain URL.<br><br>
20178  * <p>
20179  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20180  * from which the running page was served.<br><br>
20181  * <p>
20182  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20183  * <p>
20184  * Be aware that to enable the browser to parse an XML document, the server must set
20185  * the Content-Type header in the HTTP response to "text/xml".
20186  * @constructor
20187  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20188  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20189  * will be used to make the request.
20190  */
20191 Roo.data.HttpProxy = function(conn){
20192     Roo.data.HttpProxy.superclass.constructor.call(this);
20193     // is conn a conn config or a real conn?
20194     this.conn = conn;
20195     this.useAjax = !conn || !conn.events;
20196   
20197 };
20198
20199 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20200     // thse are take from connection...
20201     
20202     /**
20203      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20204      */
20205     /**
20206      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20207      * extra parameters to each request made by this object. (defaults to undefined)
20208      */
20209     /**
20210      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20211      *  to each request made by this object. (defaults to undefined)
20212      */
20213     /**
20214      * @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)
20215      */
20216     /**
20217      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20218      */
20219      /**
20220      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20221      * @type Boolean
20222      */
20223   
20224
20225     /**
20226      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20227      * @type Boolean
20228      */
20229     /**
20230      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20231      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20232      * a finer-grained basis than the DataProxy events.
20233      */
20234     getConnection : function(){
20235         return this.useAjax ? Roo.Ajax : this.conn;
20236     },
20237
20238     /**
20239      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20240      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20241      * process that block using the passed callback.
20242      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20243      * for the request to the remote server.
20244      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20245      * object into a block of Roo.data.Records.
20246      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20247      * The function must be passed <ul>
20248      * <li>The Record block object</li>
20249      * <li>The "arg" argument from the load function</li>
20250      * <li>A boolean success indicator</li>
20251      * </ul>
20252      * @param {Object} scope The scope in which to call the callback
20253      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20254      */
20255     load : function(params, reader, callback, scope, arg){
20256         if(this.fireEvent("beforeload", this, params) !== false){
20257             var  o = {
20258                 params : params || {},
20259                 request: {
20260                     callback : callback,
20261                     scope : scope,
20262                     arg : arg
20263                 },
20264                 reader: reader,
20265                 callback : this.loadResponse,
20266                 scope: this
20267             };
20268             if(this.useAjax){
20269                 Roo.applyIf(o, this.conn);
20270                 if(this.activeRequest){
20271                     Roo.Ajax.abort(this.activeRequest);
20272                 }
20273                 this.activeRequest = Roo.Ajax.request(o);
20274             }else{
20275                 this.conn.request(o);
20276             }
20277         }else{
20278             callback.call(scope||this, null, arg, false);
20279         }
20280     },
20281
20282     // private
20283     loadResponse : function(o, success, response){
20284         delete this.activeRequest;
20285         if(!success){
20286             this.fireEvent("loadexception", this, o, response);
20287             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20288             return;
20289         }
20290         var result;
20291         try {
20292             result = o.reader.read(response);
20293         }catch(e){
20294             this.fireEvent("loadexception", this, o, response, e);
20295             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20296             return;
20297         }
20298         
20299         this.fireEvent("load", this, o, o.request.arg);
20300         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20301     },
20302
20303     // private
20304     update : function(dataSet){
20305
20306     },
20307
20308     // private
20309     updateResponse : function(dataSet){
20310
20311     }
20312 });/*
20313  * Based on:
20314  * Ext JS Library 1.1.1
20315  * Copyright(c) 2006-2007, Ext JS, LLC.
20316  *
20317  * Originally Released Under LGPL - original licence link has changed is not relivant.
20318  *
20319  * Fork - LGPL
20320  * <script type="text/javascript">
20321  */
20322
20323 /**
20324  * @class Roo.data.ScriptTagProxy
20325  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20326  * other than the originating domain of the running page.<br><br>
20327  * <p>
20328  * <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
20329  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20330  * <p>
20331  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20332  * source code that is used as the source inside a &lt;script> tag.<br><br>
20333  * <p>
20334  * In order for the browser to process the returned data, the server must wrap the data object
20335  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20336  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20337  * depending on whether the callback name was passed:
20338  * <p>
20339  * <pre><code>
20340 boolean scriptTag = false;
20341 String cb = request.getParameter("callback");
20342 if (cb != null) {
20343     scriptTag = true;
20344     response.setContentType("text/javascript");
20345 } else {
20346     response.setContentType("application/x-json");
20347 }
20348 Writer out = response.getWriter();
20349 if (scriptTag) {
20350     out.write(cb + "(");
20351 }
20352 out.print(dataBlock.toJsonString());
20353 if (scriptTag) {
20354     out.write(");");
20355 }
20356 </pre></code>
20357  *
20358  * @constructor
20359  * @param {Object} config A configuration object.
20360  */
20361 Roo.data.ScriptTagProxy = function(config){
20362     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20363     Roo.apply(this, config);
20364     this.head = document.getElementsByTagName("head")[0];
20365 };
20366
20367 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20368
20369 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20370     /**
20371      * @cfg {String} url The URL from which to request the data object.
20372      */
20373     /**
20374      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20375      */
20376     timeout : 30000,
20377     /**
20378      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20379      * the server the name of the callback function set up by the load call to process the returned data object.
20380      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20381      * javascript output which calls this named function passing the data object as its only parameter.
20382      */
20383     callbackParam : "callback",
20384     /**
20385      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20386      * name to the request.
20387      */
20388     nocache : true,
20389
20390     /**
20391      * Load data from the configured URL, read the data object into
20392      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20393      * process that block using the passed callback.
20394      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20395      * for the request to the remote server.
20396      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20397      * object into a block of Roo.data.Records.
20398      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20399      * The function must be passed <ul>
20400      * <li>The Record block object</li>
20401      * <li>The "arg" argument from the load function</li>
20402      * <li>A boolean success indicator</li>
20403      * </ul>
20404      * @param {Object} scope The scope in which to call the callback
20405      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20406      */
20407     load : function(params, reader, callback, scope, arg){
20408         if(this.fireEvent("beforeload", this, params) !== false){
20409
20410             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20411
20412             var url = this.url;
20413             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20414             if(this.nocache){
20415                 url += "&_dc=" + (new Date().getTime());
20416             }
20417             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20418             var trans = {
20419                 id : transId,
20420                 cb : "stcCallback"+transId,
20421                 scriptId : "stcScript"+transId,
20422                 params : params,
20423                 arg : arg,
20424                 url : url,
20425                 callback : callback,
20426                 scope : scope,
20427                 reader : reader
20428             };
20429             var conn = this;
20430
20431             window[trans.cb] = function(o){
20432                 conn.handleResponse(o, trans);
20433             };
20434
20435             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20436
20437             if(this.autoAbort !== false){
20438                 this.abort();
20439             }
20440
20441             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20442
20443             var script = document.createElement("script");
20444             script.setAttribute("src", url);
20445             script.setAttribute("type", "text/javascript");
20446             script.setAttribute("id", trans.scriptId);
20447             this.head.appendChild(script);
20448
20449             this.trans = trans;
20450         }else{
20451             callback.call(scope||this, null, arg, false);
20452         }
20453     },
20454
20455     // private
20456     isLoading : function(){
20457         return this.trans ? true : false;
20458     },
20459
20460     /**
20461      * Abort the current server request.
20462      */
20463     abort : function(){
20464         if(this.isLoading()){
20465             this.destroyTrans(this.trans);
20466         }
20467     },
20468
20469     // private
20470     destroyTrans : function(trans, isLoaded){
20471         this.head.removeChild(document.getElementById(trans.scriptId));
20472         clearTimeout(trans.timeoutId);
20473         if(isLoaded){
20474             window[trans.cb] = undefined;
20475             try{
20476                 delete window[trans.cb];
20477             }catch(e){}
20478         }else{
20479             // if hasn't been loaded, wait for load to remove it to prevent script error
20480             window[trans.cb] = function(){
20481                 window[trans.cb] = undefined;
20482                 try{
20483                     delete window[trans.cb];
20484                 }catch(e){}
20485             };
20486         }
20487     },
20488
20489     // private
20490     handleResponse : function(o, trans){
20491         this.trans = false;
20492         this.destroyTrans(trans, true);
20493         var result;
20494         try {
20495             result = trans.reader.readRecords(o);
20496         }catch(e){
20497             this.fireEvent("loadexception", this, o, trans.arg, e);
20498             trans.callback.call(trans.scope||window, null, trans.arg, false);
20499             return;
20500         }
20501         this.fireEvent("load", this, o, trans.arg);
20502         trans.callback.call(trans.scope||window, result, trans.arg, true);
20503     },
20504
20505     // private
20506     handleFailure : function(trans){
20507         this.trans = false;
20508         this.destroyTrans(trans, false);
20509         this.fireEvent("loadexception", this, null, trans.arg);
20510         trans.callback.call(trans.scope||window, null, trans.arg, false);
20511     }
20512 });/*
20513  * Based on:
20514  * Ext JS Library 1.1.1
20515  * Copyright(c) 2006-2007, Ext JS, LLC.
20516  *
20517  * Originally Released Under LGPL - original licence link has changed is not relivant.
20518  *
20519  * Fork - LGPL
20520  * <script type="text/javascript">
20521  */
20522
20523 /**
20524  * @class Roo.data.JsonReader
20525  * @extends Roo.data.DataReader
20526  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20527  * based on mappings in a provided Roo.data.Record constructor.
20528  * 
20529  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20530  * in the reply previously. 
20531  * 
20532  * <p>
20533  * Example code:
20534  * <pre><code>
20535 var RecordDef = Roo.data.Record.create([
20536     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20537     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20538 ]);
20539 var myReader = new Roo.data.JsonReader({
20540     totalProperty: "results",    // The property which contains the total dataset size (optional)
20541     root: "rows",                // The property which contains an Array of row objects
20542     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20543 }, RecordDef);
20544 </code></pre>
20545  * <p>
20546  * This would consume a JSON file like this:
20547  * <pre><code>
20548 { 'results': 2, 'rows': [
20549     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20550     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20551 }
20552 </code></pre>
20553  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20554  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20555  * paged from the remote server.
20556  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20557  * @cfg {String} root name of the property which contains the Array of row objects.
20558  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20559  * @constructor
20560  * Create a new JsonReader
20561  * @param {Object} meta Metadata configuration options
20562  * @param {Object} recordType Either an Array of field definition objects,
20563  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20564  */
20565 Roo.data.JsonReader = function(meta, recordType){
20566     
20567     meta = meta || {};
20568     // set some defaults:
20569     Roo.applyIf(meta, {
20570         totalProperty: 'total',
20571         successProperty : 'success',
20572         root : 'data',
20573         id : 'id'
20574     });
20575     
20576     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20577 };
20578 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20579     
20580     /**
20581      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20582      * Used by Store query builder to append _requestMeta to params.
20583      * 
20584      */
20585     metaFromRemote : false,
20586     /**
20587      * This method is only used by a DataProxy which has retrieved data from a remote server.
20588      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20589      * @return {Object} data A data block which is used by an Roo.data.Store object as
20590      * a cache of Roo.data.Records.
20591      */
20592     read : function(response){
20593         var json = response.responseText;
20594        
20595         var o = /* eval:var:o */ eval("("+json+")");
20596         if(!o) {
20597             throw {message: "JsonReader.read: Json object not found"};
20598         }
20599         
20600         if(o.metaData){
20601             
20602             delete this.ef;
20603             this.metaFromRemote = true;
20604             this.meta = o.metaData;
20605             this.recordType = Roo.data.Record.create(o.metaData.fields);
20606             this.onMetaChange(this.meta, this.recordType, o);
20607         }
20608         return this.readRecords(o);
20609     },
20610
20611     // private function a store will implement
20612     onMetaChange : function(meta, recordType, o){
20613
20614     },
20615
20616     /**
20617          * @ignore
20618          */
20619     simpleAccess: function(obj, subsc) {
20620         return obj[subsc];
20621     },
20622
20623         /**
20624          * @ignore
20625          */
20626     getJsonAccessor: function(){
20627         var re = /[\[\.]/;
20628         return function(expr) {
20629             try {
20630                 return(re.test(expr))
20631                     ? new Function("obj", "return obj." + expr)
20632                     : function(obj){
20633                         return obj[expr];
20634                     };
20635             } catch(e){}
20636             return Roo.emptyFn;
20637         };
20638     }(),
20639
20640     /**
20641      * Create a data block containing Roo.data.Records from an XML document.
20642      * @param {Object} o An object which contains an Array of row objects in the property specified
20643      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20644      * which contains the total size of the dataset.
20645      * @return {Object} data A data block which is used by an Roo.data.Store object as
20646      * a cache of Roo.data.Records.
20647      */
20648     readRecords : function(o){
20649         /**
20650          * After any data loads, the raw JSON data is available for further custom processing.
20651          * @type Object
20652          */
20653         this.jsonData = o;
20654         var s = this.meta, Record = this.recordType,
20655             f = Record.prototype.fields, fi = f.items, fl = f.length;
20656
20657 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20658         if (!this.ef) {
20659             if(s.totalProperty) {
20660                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20661                 }
20662                 if(s.successProperty) {
20663                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20664                 }
20665                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20666                 if (s.id) {
20667                         var g = this.getJsonAccessor(s.id);
20668                         this.getId = function(rec) {
20669                                 var r = g(rec);
20670                                 return (r === undefined || r === "") ? null : r;
20671                         };
20672                 } else {
20673                         this.getId = function(){return null;};
20674                 }
20675             this.ef = [];
20676             for(var jj = 0; jj < fl; jj++){
20677                 f = fi[jj];
20678                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20679                 this.ef[jj] = this.getJsonAccessor(map);
20680             }
20681         }
20682
20683         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20684         if(s.totalProperty){
20685             var vt = parseInt(this.getTotal(o), 10);
20686             if(!isNaN(vt)){
20687                 totalRecords = vt;
20688             }
20689         }
20690         if(s.successProperty){
20691             var vs = this.getSuccess(o);
20692             if(vs === false || vs === 'false'){
20693                 success = false;
20694             }
20695         }
20696         var records = [];
20697             for(var i = 0; i < c; i++){
20698                     var n = root[i];
20699                 var values = {};
20700                 var id = this.getId(n);
20701                 for(var j = 0; j < fl; j++){
20702                     f = fi[j];
20703                 var v = this.ef[j](n);
20704                 if (!f.convert) {
20705                     Roo.log('missing convert for ' + f.name);
20706                     Roo.log(f);
20707                     continue;
20708                 }
20709                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20710                 }
20711                 var record = new Record(values, id);
20712                 record.json = n;
20713                 records[i] = record;
20714             }
20715             return {
20716                 success : success,
20717                 records : records,
20718                 totalRecords : totalRecords
20719             };
20720     }
20721 });/*
20722  * Based on:
20723  * Ext JS Library 1.1.1
20724  * Copyright(c) 2006-2007, Ext JS, LLC.
20725  *
20726  * Originally Released Under LGPL - original licence link has changed is not relivant.
20727  *
20728  * Fork - LGPL
20729  * <script type="text/javascript">
20730  */
20731
20732 /**
20733  * @class Roo.data.XmlReader
20734  * @extends Roo.data.DataReader
20735  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20736  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20737  * <p>
20738  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20739  * header in the HTTP response must be set to "text/xml".</em>
20740  * <p>
20741  * Example code:
20742  * <pre><code>
20743 var RecordDef = Roo.data.Record.create([
20744    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20745    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20746 ]);
20747 var myReader = new Roo.data.XmlReader({
20748    totalRecords: "results", // The element which contains the total dataset size (optional)
20749    record: "row",           // The repeated element which contains row information
20750    id: "id"                 // The element within the row that provides an ID for the record (optional)
20751 }, RecordDef);
20752 </code></pre>
20753  * <p>
20754  * This would consume an XML file like this:
20755  * <pre><code>
20756 &lt;?xml?>
20757 &lt;dataset>
20758  &lt;results>2&lt;/results>
20759  &lt;row>
20760    &lt;id>1&lt;/id>
20761    &lt;name>Bill&lt;/name>
20762    &lt;occupation>Gardener&lt;/occupation>
20763  &lt;/row>
20764  &lt;row>
20765    &lt;id>2&lt;/id>
20766    &lt;name>Ben&lt;/name>
20767    &lt;occupation>Horticulturalist&lt;/occupation>
20768  &lt;/row>
20769 &lt;/dataset>
20770 </code></pre>
20771  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20772  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20773  * paged from the remote server.
20774  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20775  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20776  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20777  * a record identifier value.
20778  * @constructor
20779  * Create a new XmlReader
20780  * @param {Object} meta Metadata configuration options
20781  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20782  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20783  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20784  */
20785 Roo.data.XmlReader = function(meta, recordType){
20786     meta = meta || {};
20787     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20788 };
20789 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20790     /**
20791      * This method is only used by a DataProxy which has retrieved data from a remote server.
20792          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20793          * to contain a method called 'responseXML' that returns an XML document object.
20794      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20795      * a cache of Roo.data.Records.
20796      */
20797     read : function(response){
20798         var doc = response.responseXML;
20799         if(!doc) {
20800             throw {message: "XmlReader.read: XML Document not available"};
20801         }
20802         return this.readRecords(doc);
20803     },
20804
20805     /**
20806      * Create a data block containing Roo.data.Records from an XML document.
20807          * @param {Object} doc A parsed XML document.
20808      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20809      * a cache of Roo.data.Records.
20810      */
20811     readRecords : function(doc){
20812         /**
20813          * After any data loads/reads, the raw XML Document is available for further custom processing.
20814          * @type XMLDocument
20815          */
20816         this.xmlData = doc;
20817         var root = doc.documentElement || doc;
20818         var q = Roo.DomQuery;
20819         var recordType = this.recordType, fields = recordType.prototype.fields;
20820         var sid = this.meta.id;
20821         var totalRecords = 0, success = true;
20822         if(this.meta.totalRecords){
20823             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20824         }
20825         
20826         if(this.meta.success){
20827             var sv = q.selectValue(this.meta.success, root, true);
20828             success = sv !== false && sv !== 'false';
20829         }
20830         var records = [];
20831         var ns = q.select(this.meta.record, root);
20832         for(var i = 0, len = ns.length; i < len; i++) {
20833                 var n = ns[i];
20834                 var values = {};
20835                 var id = sid ? q.selectValue(sid, n) : undefined;
20836                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20837                     var f = fields.items[j];
20838                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20839                     v = f.convert(v);
20840                     values[f.name] = v;
20841                 }
20842                 var record = new recordType(values, id);
20843                 record.node = n;
20844                 records[records.length] = record;
20845             }
20846
20847             return {
20848                 success : success,
20849                 records : records,
20850                 totalRecords : totalRecords || records.length
20851             };
20852     }
20853 });/*
20854  * Based on:
20855  * Ext JS Library 1.1.1
20856  * Copyright(c) 2006-2007, Ext JS, LLC.
20857  *
20858  * Originally Released Under LGPL - original licence link has changed is not relivant.
20859  *
20860  * Fork - LGPL
20861  * <script type="text/javascript">
20862  */
20863
20864 /**
20865  * @class Roo.data.ArrayReader
20866  * @extends Roo.data.DataReader
20867  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20868  * Each element of that Array represents a row of data fields. The
20869  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20870  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20871  * <p>
20872  * Example code:.
20873  * <pre><code>
20874 var RecordDef = Roo.data.Record.create([
20875     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20876     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20877 ]);
20878 var myReader = new Roo.data.ArrayReader({
20879     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20880 }, RecordDef);
20881 </code></pre>
20882  * <p>
20883  * This would consume an Array like this:
20884  * <pre><code>
20885 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20886   </code></pre>
20887  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20888  * @constructor
20889  * Create a new JsonReader
20890  * @param {Object} meta Metadata configuration options.
20891  * @param {Object} recordType Either an Array of field definition objects
20892  * as specified to {@link Roo.data.Record#create},
20893  * or an {@link Roo.data.Record} object
20894  * created using {@link Roo.data.Record#create}.
20895  */
20896 Roo.data.ArrayReader = function(meta, recordType){
20897     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20898 };
20899
20900 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20901     /**
20902      * Create a data block containing Roo.data.Records from an XML document.
20903      * @param {Object} o An Array of row objects which represents the dataset.
20904      * @return {Object} data A data block which is used by an Roo.data.Store object as
20905      * a cache of Roo.data.Records.
20906      */
20907     readRecords : function(o){
20908         var sid = this.meta ? this.meta.id : null;
20909         var recordType = this.recordType, fields = recordType.prototype.fields;
20910         var records = [];
20911         var root = o;
20912             for(var i = 0; i < root.length; i++){
20913                     var n = root[i];
20914                 var values = {};
20915                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20916                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20917                 var f = fields.items[j];
20918                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20919                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20920                 v = f.convert(v);
20921                 values[f.name] = v;
20922             }
20923                 var record = new recordType(values, id);
20924                 record.json = n;
20925                 records[records.length] = record;
20926             }
20927             return {
20928                 records : records,
20929                 totalRecords : records.length
20930             };
20931     }
20932 });/*
20933  * Based on:
20934  * Ext JS Library 1.1.1
20935  * Copyright(c) 2006-2007, Ext JS, LLC.
20936  *
20937  * Originally Released Under LGPL - original licence link has changed is not relivant.
20938  *
20939  * Fork - LGPL
20940  * <script type="text/javascript">
20941  */
20942
20943
20944 /**
20945  * @class Roo.data.Tree
20946  * @extends Roo.util.Observable
20947  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20948  * in the tree have most standard DOM functionality.
20949  * @constructor
20950  * @param {Node} root (optional) The root node
20951  */
20952 Roo.data.Tree = function(root){
20953    this.nodeHash = {};
20954    /**
20955     * The root node for this tree
20956     * @type Node
20957     */
20958    this.root = null;
20959    if(root){
20960        this.setRootNode(root);
20961    }
20962    this.addEvents({
20963        /**
20964         * @event append
20965         * Fires when a new child node is appended to a node in this tree.
20966         * @param {Tree} tree The owner tree
20967         * @param {Node} parent The parent node
20968         * @param {Node} node The newly appended node
20969         * @param {Number} index The index of the newly appended node
20970         */
20971        "append" : true,
20972        /**
20973         * @event remove
20974         * Fires when a child node is removed from a node in this tree.
20975         * @param {Tree} tree The owner tree
20976         * @param {Node} parent The parent node
20977         * @param {Node} node The child node removed
20978         */
20979        "remove" : true,
20980        /**
20981         * @event move
20982         * Fires when a node is moved to a new location in the tree
20983         * @param {Tree} tree The owner tree
20984         * @param {Node} node The node moved
20985         * @param {Node} oldParent The old parent of this node
20986         * @param {Node} newParent The new parent of this node
20987         * @param {Number} index The index it was moved to
20988         */
20989        "move" : true,
20990        /**
20991         * @event insert
20992         * Fires when a new child node is inserted in a node in this tree.
20993         * @param {Tree} tree The owner tree
20994         * @param {Node} parent The parent node
20995         * @param {Node} node The child node inserted
20996         * @param {Node} refNode The child node the node was inserted before
20997         */
20998        "insert" : true,
20999        /**
21000         * @event beforeappend
21001         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21002         * @param {Tree} tree The owner tree
21003         * @param {Node} parent The parent node
21004         * @param {Node} node The child node to be appended
21005         */
21006        "beforeappend" : true,
21007        /**
21008         * @event beforeremove
21009         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21010         * @param {Tree} tree The owner tree
21011         * @param {Node} parent The parent node
21012         * @param {Node} node The child node to be removed
21013         */
21014        "beforeremove" : true,
21015        /**
21016         * @event beforemove
21017         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21018         * @param {Tree} tree The owner tree
21019         * @param {Node} node The node being moved
21020         * @param {Node} oldParent The parent of the node
21021         * @param {Node} newParent The new parent the node is moving to
21022         * @param {Number} index The index it is being moved to
21023         */
21024        "beforemove" : true,
21025        /**
21026         * @event beforeinsert
21027         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21028         * @param {Tree} tree The owner tree
21029         * @param {Node} parent The parent node
21030         * @param {Node} node The child node to be inserted
21031         * @param {Node} refNode The child node the node is being inserted before
21032         */
21033        "beforeinsert" : true
21034    });
21035
21036     Roo.data.Tree.superclass.constructor.call(this);
21037 };
21038
21039 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21040     pathSeparator: "/",
21041
21042     proxyNodeEvent : function(){
21043         return this.fireEvent.apply(this, arguments);
21044     },
21045
21046     /**
21047      * Returns the root node for this tree.
21048      * @return {Node}
21049      */
21050     getRootNode : function(){
21051         return this.root;
21052     },
21053
21054     /**
21055      * Sets the root node for this tree.
21056      * @param {Node} node
21057      * @return {Node}
21058      */
21059     setRootNode : function(node){
21060         this.root = node;
21061         node.ownerTree = this;
21062         node.isRoot = true;
21063         this.registerNode(node);
21064         return node;
21065     },
21066
21067     /**
21068      * Gets a node in this tree by its id.
21069      * @param {String} id
21070      * @return {Node}
21071      */
21072     getNodeById : function(id){
21073         return this.nodeHash[id];
21074     },
21075
21076     registerNode : function(node){
21077         this.nodeHash[node.id] = node;
21078     },
21079
21080     unregisterNode : function(node){
21081         delete this.nodeHash[node.id];
21082     },
21083
21084     toString : function(){
21085         return "[Tree"+(this.id?" "+this.id:"")+"]";
21086     }
21087 });
21088
21089 /**
21090  * @class Roo.data.Node
21091  * @extends Roo.util.Observable
21092  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21093  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21094  * @constructor
21095  * @param {Object} attributes The attributes/config for the node
21096  */
21097 Roo.data.Node = function(attributes){
21098     /**
21099      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21100      * @type {Object}
21101      */
21102     this.attributes = attributes || {};
21103     this.leaf = this.attributes.leaf;
21104     /**
21105      * The node id. @type String
21106      */
21107     this.id = this.attributes.id;
21108     if(!this.id){
21109         this.id = Roo.id(null, "ynode-");
21110         this.attributes.id = this.id;
21111     }
21112     /**
21113      * All child nodes of this node. @type Array
21114      */
21115     this.childNodes = [];
21116     if(!this.childNodes.indexOf){ // indexOf is a must
21117         this.childNodes.indexOf = function(o){
21118             for(var i = 0, len = this.length; i < len; i++){
21119                 if(this[i] == o) {
21120                     return i;
21121                 }
21122             }
21123             return -1;
21124         };
21125     }
21126     /**
21127      * The parent node for this node. @type Node
21128      */
21129     this.parentNode = null;
21130     /**
21131      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21132      */
21133     this.firstChild = null;
21134     /**
21135      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21136      */
21137     this.lastChild = null;
21138     /**
21139      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21140      */
21141     this.previousSibling = null;
21142     /**
21143      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21144      */
21145     this.nextSibling = null;
21146
21147     this.addEvents({
21148        /**
21149         * @event append
21150         * Fires when a new child node is appended
21151         * @param {Tree} tree The owner tree
21152         * @param {Node} this This node
21153         * @param {Node} node The newly appended node
21154         * @param {Number} index The index of the newly appended node
21155         */
21156        "append" : true,
21157        /**
21158         * @event remove
21159         * Fires when a child node is removed
21160         * @param {Tree} tree The owner tree
21161         * @param {Node} this This node
21162         * @param {Node} node The removed node
21163         */
21164        "remove" : true,
21165        /**
21166         * @event move
21167         * Fires when this node is moved to a new location in the tree
21168         * @param {Tree} tree The owner tree
21169         * @param {Node} this This node
21170         * @param {Node} oldParent The old parent of this node
21171         * @param {Node} newParent The new parent of this node
21172         * @param {Number} index The index it was moved to
21173         */
21174        "move" : true,
21175        /**
21176         * @event insert
21177         * Fires when a new child node is inserted.
21178         * @param {Tree} tree The owner tree
21179         * @param {Node} this This node
21180         * @param {Node} node The child node inserted
21181         * @param {Node} refNode The child node the node was inserted before
21182         */
21183        "insert" : true,
21184        /**
21185         * @event beforeappend
21186         * Fires before a new child is appended, return false to cancel the append.
21187         * @param {Tree} tree The owner tree
21188         * @param {Node} this This node
21189         * @param {Node} node The child node to be appended
21190         */
21191        "beforeappend" : true,
21192        /**
21193         * @event beforeremove
21194         * Fires before a child is removed, return false to cancel the remove.
21195         * @param {Tree} tree The owner tree
21196         * @param {Node} this This node
21197         * @param {Node} node The child node to be removed
21198         */
21199        "beforeremove" : true,
21200        /**
21201         * @event beforemove
21202         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21203         * @param {Tree} tree The owner tree
21204         * @param {Node} this This node
21205         * @param {Node} oldParent The parent of this node
21206         * @param {Node} newParent The new parent this node is moving to
21207         * @param {Number} index The index it is being moved to
21208         */
21209        "beforemove" : true,
21210        /**
21211         * @event beforeinsert
21212         * Fires before a new child is inserted, return false to cancel the insert.
21213         * @param {Tree} tree The owner tree
21214         * @param {Node} this This node
21215         * @param {Node} node The child node to be inserted
21216         * @param {Node} refNode The child node the node is being inserted before
21217         */
21218        "beforeinsert" : true
21219    });
21220     this.listeners = this.attributes.listeners;
21221     Roo.data.Node.superclass.constructor.call(this);
21222 };
21223
21224 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21225     fireEvent : function(evtName){
21226         // first do standard event for this node
21227         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21228             return false;
21229         }
21230         // then bubble it up to the tree if the event wasn't cancelled
21231         var ot = this.getOwnerTree();
21232         if(ot){
21233             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21234                 return false;
21235             }
21236         }
21237         return true;
21238     },
21239
21240     /**
21241      * Returns true if this node is a leaf
21242      * @return {Boolean}
21243      */
21244     isLeaf : function(){
21245         return this.leaf === true;
21246     },
21247
21248     // private
21249     setFirstChild : function(node){
21250         this.firstChild = node;
21251     },
21252
21253     //private
21254     setLastChild : function(node){
21255         this.lastChild = node;
21256     },
21257
21258
21259     /**
21260      * Returns true if this node is the last child of its parent
21261      * @return {Boolean}
21262      */
21263     isLast : function(){
21264        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21265     },
21266
21267     /**
21268      * Returns true if this node is the first child of its parent
21269      * @return {Boolean}
21270      */
21271     isFirst : function(){
21272        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21273     },
21274
21275     hasChildNodes : function(){
21276         return !this.isLeaf() && this.childNodes.length > 0;
21277     },
21278
21279     /**
21280      * Insert node(s) as the last child node of this node.
21281      * @param {Node/Array} node The node or Array of nodes to append
21282      * @return {Node} The appended node if single append, or null if an array was passed
21283      */
21284     appendChild : function(node){
21285         var multi = false;
21286         if(node instanceof Array){
21287             multi = node;
21288         }else if(arguments.length > 1){
21289             multi = arguments;
21290         }
21291         // if passed an array or multiple args do them one by one
21292         if(multi){
21293             for(var i = 0, len = multi.length; i < len; i++) {
21294                 this.appendChild(multi[i]);
21295             }
21296         }else{
21297             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21298                 return false;
21299             }
21300             var index = this.childNodes.length;
21301             var oldParent = node.parentNode;
21302             // it's a move, make sure we move it cleanly
21303             if(oldParent){
21304                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21305                     return false;
21306                 }
21307                 oldParent.removeChild(node);
21308             }
21309             index = this.childNodes.length;
21310             if(index == 0){
21311                 this.setFirstChild(node);
21312             }
21313             this.childNodes.push(node);
21314             node.parentNode = this;
21315             var ps = this.childNodes[index-1];
21316             if(ps){
21317                 node.previousSibling = ps;
21318                 ps.nextSibling = node;
21319             }else{
21320                 node.previousSibling = null;
21321             }
21322             node.nextSibling = null;
21323             this.setLastChild(node);
21324             node.setOwnerTree(this.getOwnerTree());
21325             this.fireEvent("append", this.ownerTree, this, node, index);
21326             if(oldParent){
21327                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21328             }
21329             return node;
21330         }
21331     },
21332
21333     /**
21334      * Removes a child node from this node.
21335      * @param {Node} node The node to remove
21336      * @return {Node} The removed node
21337      */
21338     removeChild : function(node){
21339         var index = this.childNodes.indexOf(node);
21340         if(index == -1){
21341             return false;
21342         }
21343         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21344             return false;
21345         }
21346
21347         // remove it from childNodes collection
21348         this.childNodes.splice(index, 1);
21349
21350         // update siblings
21351         if(node.previousSibling){
21352             node.previousSibling.nextSibling = node.nextSibling;
21353         }
21354         if(node.nextSibling){
21355             node.nextSibling.previousSibling = node.previousSibling;
21356         }
21357
21358         // update child refs
21359         if(this.firstChild == node){
21360             this.setFirstChild(node.nextSibling);
21361         }
21362         if(this.lastChild == node){
21363             this.setLastChild(node.previousSibling);
21364         }
21365
21366         node.setOwnerTree(null);
21367         // clear any references from the node
21368         node.parentNode = null;
21369         node.previousSibling = null;
21370         node.nextSibling = null;
21371         this.fireEvent("remove", this.ownerTree, this, node);
21372         return node;
21373     },
21374
21375     /**
21376      * Inserts the first node before the second node in this nodes childNodes collection.
21377      * @param {Node} node The node to insert
21378      * @param {Node} refNode The node to insert before (if null the node is appended)
21379      * @return {Node} The inserted node
21380      */
21381     insertBefore : function(node, refNode){
21382         if(!refNode){ // like standard Dom, refNode can be null for append
21383             return this.appendChild(node);
21384         }
21385         // nothing to do
21386         if(node == refNode){
21387             return false;
21388         }
21389
21390         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21391             return false;
21392         }
21393         var index = this.childNodes.indexOf(refNode);
21394         var oldParent = node.parentNode;
21395         var refIndex = index;
21396
21397         // when moving internally, indexes will change after remove
21398         if(oldParent == this && this.childNodes.indexOf(node) < index){
21399             refIndex--;
21400         }
21401
21402         // it's a move, make sure we move it cleanly
21403         if(oldParent){
21404             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21405                 return false;
21406             }
21407             oldParent.removeChild(node);
21408         }
21409         if(refIndex == 0){
21410             this.setFirstChild(node);
21411         }
21412         this.childNodes.splice(refIndex, 0, node);
21413         node.parentNode = this;
21414         var ps = this.childNodes[refIndex-1];
21415         if(ps){
21416             node.previousSibling = ps;
21417             ps.nextSibling = node;
21418         }else{
21419             node.previousSibling = null;
21420         }
21421         node.nextSibling = refNode;
21422         refNode.previousSibling = node;
21423         node.setOwnerTree(this.getOwnerTree());
21424         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21425         if(oldParent){
21426             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21427         }
21428         return node;
21429     },
21430
21431     /**
21432      * Returns the child node at the specified index.
21433      * @param {Number} index
21434      * @return {Node}
21435      */
21436     item : function(index){
21437         return this.childNodes[index];
21438     },
21439
21440     /**
21441      * Replaces one child node in this node with another.
21442      * @param {Node} newChild The replacement node
21443      * @param {Node} oldChild The node to replace
21444      * @return {Node} The replaced node
21445      */
21446     replaceChild : function(newChild, oldChild){
21447         this.insertBefore(newChild, oldChild);
21448         this.removeChild(oldChild);
21449         return oldChild;
21450     },
21451
21452     /**
21453      * Returns the index of a child node
21454      * @param {Node} node
21455      * @return {Number} The index of the node or -1 if it was not found
21456      */
21457     indexOf : function(child){
21458         return this.childNodes.indexOf(child);
21459     },
21460
21461     /**
21462      * Returns the tree this node is in.
21463      * @return {Tree}
21464      */
21465     getOwnerTree : function(){
21466         // if it doesn't have one, look for one
21467         if(!this.ownerTree){
21468             var p = this;
21469             while(p){
21470                 if(p.ownerTree){
21471                     this.ownerTree = p.ownerTree;
21472                     break;
21473                 }
21474                 p = p.parentNode;
21475             }
21476         }
21477         return this.ownerTree;
21478     },
21479
21480     /**
21481      * Returns depth of this node (the root node has a depth of 0)
21482      * @return {Number}
21483      */
21484     getDepth : function(){
21485         var depth = 0;
21486         var p = this;
21487         while(p.parentNode){
21488             ++depth;
21489             p = p.parentNode;
21490         }
21491         return depth;
21492     },
21493
21494     // private
21495     setOwnerTree : function(tree){
21496         // if it's move, we need to update everyone
21497         if(tree != this.ownerTree){
21498             if(this.ownerTree){
21499                 this.ownerTree.unregisterNode(this);
21500             }
21501             this.ownerTree = tree;
21502             var cs = this.childNodes;
21503             for(var i = 0, len = cs.length; i < len; i++) {
21504                 cs[i].setOwnerTree(tree);
21505             }
21506             if(tree){
21507                 tree.registerNode(this);
21508             }
21509         }
21510     },
21511
21512     /**
21513      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21514      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21515      * @return {String} The path
21516      */
21517     getPath : function(attr){
21518         attr = attr || "id";
21519         var p = this.parentNode;
21520         var b = [this.attributes[attr]];
21521         while(p){
21522             b.unshift(p.attributes[attr]);
21523             p = p.parentNode;
21524         }
21525         var sep = this.getOwnerTree().pathSeparator;
21526         return sep + b.join(sep);
21527     },
21528
21529     /**
21530      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21531      * function call will be the scope provided or the current node. The arguments to the function
21532      * will be the args provided or the current node. If the function returns false at any point,
21533      * the bubble is stopped.
21534      * @param {Function} fn The function to call
21535      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21536      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21537      */
21538     bubble : function(fn, scope, args){
21539         var p = this;
21540         while(p){
21541             if(fn.call(scope || p, args || p) === false){
21542                 break;
21543             }
21544             p = p.parentNode;
21545         }
21546     },
21547
21548     /**
21549      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21550      * function call will be the scope provided or the current node. The arguments to the function
21551      * will be the args provided or the current node. If the function returns false at any point,
21552      * the cascade is stopped on that branch.
21553      * @param {Function} fn The function to call
21554      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21555      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21556      */
21557     cascade : function(fn, scope, args){
21558         if(fn.call(scope || this, args || this) !== false){
21559             var cs = this.childNodes;
21560             for(var i = 0, len = cs.length; i < len; i++) {
21561                 cs[i].cascade(fn, scope, args);
21562             }
21563         }
21564     },
21565
21566     /**
21567      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21568      * function call will be the scope provided or the current node. The arguments to the function
21569      * will be the args provided or the current node. If the function returns false at any point,
21570      * the iteration stops.
21571      * @param {Function} fn The function to call
21572      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21573      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21574      */
21575     eachChild : function(fn, scope, args){
21576         var cs = this.childNodes;
21577         for(var i = 0, len = cs.length; i < len; i++) {
21578                 if(fn.call(scope || this, args || cs[i]) === false){
21579                     break;
21580                 }
21581         }
21582     },
21583
21584     /**
21585      * Finds the first child that has the attribute with the specified value.
21586      * @param {String} attribute The attribute name
21587      * @param {Mixed} value The value to search for
21588      * @return {Node} The found child or null if none was found
21589      */
21590     findChild : function(attribute, value){
21591         var cs = this.childNodes;
21592         for(var i = 0, len = cs.length; i < len; i++) {
21593                 if(cs[i].attributes[attribute] == value){
21594                     return cs[i];
21595                 }
21596         }
21597         return null;
21598     },
21599
21600     /**
21601      * Finds the first child by a custom function. The child matches if the function passed
21602      * returns true.
21603      * @param {Function} fn
21604      * @param {Object} scope (optional)
21605      * @return {Node} The found child or null if none was found
21606      */
21607     findChildBy : function(fn, scope){
21608         var cs = this.childNodes;
21609         for(var i = 0, len = cs.length; i < len; i++) {
21610                 if(fn.call(scope||cs[i], cs[i]) === true){
21611                     return cs[i];
21612                 }
21613         }
21614         return null;
21615     },
21616
21617     /**
21618      * Sorts this nodes children using the supplied sort function
21619      * @param {Function} fn
21620      * @param {Object} scope (optional)
21621      */
21622     sort : function(fn, scope){
21623         var cs = this.childNodes;
21624         var len = cs.length;
21625         if(len > 0){
21626             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21627             cs.sort(sortFn);
21628             for(var i = 0; i < len; i++){
21629                 var n = cs[i];
21630                 n.previousSibling = cs[i-1];
21631                 n.nextSibling = cs[i+1];
21632                 if(i == 0){
21633                     this.setFirstChild(n);
21634                 }
21635                 if(i == len-1){
21636                     this.setLastChild(n);
21637                 }
21638             }
21639         }
21640     },
21641
21642     /**
21643      * Returns true if this node is an ancestor (at any point) of the passed node.
21644      * @param {Node} node
21645      * @return {Boolean}
21646      */
21647     contains : function(node){
21648         return node.isAncestor(this);
21649     },
21650
21651     /**
21652      * Returns true if the passed node is an ancestor (at any point) of this node.
21653      * @param {Node} node
21654      * @return {Boolean}
21655      */
21656     isAncestor : function(node){
21657         var p = this.parentNode;
21658         while(p){
21659             if(p == node){
21660                 return true;
21661             }
21662             p = p.parentNode;
21663         }
21664         return false;
21665     },
21666
21667     toString : function(){
21668         return "[Node"+(this.id?" "+this.id:"")+"]";
21669     }
21670 });/*
21671  * Based on:
21672  * Ext JS Library 1.1.1
21673  * Copyright(c) 2006-2007, Ext JS, LLC.
21674  *
21675  * Originally Released Under LGPL - original licence link has changed is not relivant.
21676  *
21677  * Fork - LGPL
21678  * <script type="text/javascript">
21679  */
21680  
21681
21682 /**
21683  * @class Roo.ComponentMgr
21684  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21685  * @singleton
21686  */
21687 Roo.ComponentMgr = function(){
21688     var all = new Roo.util.MixedCollection();
21689
21690     return {
21691         /**
21692          * Registers a component.
21693          * @param {Roo.Component} c The component
21694          */
21695         register : function(c){
21696             all.add(c);
21697         },
21698
21699         /**
21700          * Unregisters a component.
21701          * @param {Roo.Component} c The component
21702          */
21703         unregister : function(c){
21704             all.remove(c);
21705         },
21706
21707         /**
21708          * Returns a component by id
21709          * @param {String} id The component id
21710          */
21711         get : function(id){
21712             return all.get(id);
21713         },
21714
21715         /**
21716          * Registers a function that will be called when a specified component is added to ComponentMgr
21717          * @param {String} id The component id
21718          * @param {Funtction} fn The callback function
21719          * @param {Object} scope The scope of the callback
21720          */
21721         onAvailable : function(id, fn, scope){
21722             all.on("add", function(index, o){
21723                 if(o.id == id){
21724                     fn.call(scope || o, o);
21725                     all.un("add", fn, scope);
21726                 }
21727             });
21728         }
21729     };
21730 }();/*
21731  * Based on:
21732  * Ext JS Library 1.1.1
21733  * Copyright(c) 2006-2007, Ext JS, LLC.
21734  *
21735  * Originally Released Under LGPL - original licence link has changed is not relivant.
21736  *
21737  * Fork - LGPL
21738  * <script type="text/javascript">
21739  */
21740  
21741 /**
21742  * @class Roo.Component
21743  * @extends Roo.util.Observable
21744  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21745  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21746  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21747  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21748  * All visual components (widgets) that require rendering into a layout should subclass Component.
21749  * @constructor
21750  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21751  * 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
21752  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21753  */
21754 Roo.Component = function(config){
21755     config = config || {};
21756     if(config.tagName || config.dom || typeof config == "string"){ // element object
21757         config = {el: config, id: config.id || config};
21758     }
21759     this.initialConfig = config;
21760
21761     Roo.apply(this, config);
21762     this.addEvents({
21763         /**
21764          * @event disable
21765          * Fires after the component is disabled.
21766              * @param {Roo.Component} this
21767              */
21768         disable : true,
21769         /**
21770          * @event enable
21771          * Fires after the component is enabled.
21772              * @param {Roo.Component} this
21773              */
21774         enable : true,
21775         /**
21776          * @event beforeshow
21777          * Fires before the component is shown.  Return false to stop the show.
21778              * @param {Roo.Component} this
21779              */
21780         beforeshow : true,
21781         /**
21782          * @event show
21783          * Fires after the component is shown.
21784              * @param {Roo.Component} this
21785              */
21786         show : true,
21787         /**
21788          * @event beforehide
21789          * Fires before the component is hidden. Return false to stop the hide.
21790              * @param {Roo.Component} this
21791              */
21792         beforehide : true,
21793         /**
21794          * @event hide
21795          * Fires after the component is hidden.
21796              * @param {Roo.Component} this
21797              */
21798         hide : true,
21799         /**
21800          * @event beforerender
21801          * Fires before the component is rendered. Return false to stop the render.
21802              * @param {Roo.Component} this
21803              */
21804         beforerender : true,
21805         /**
21806          * @event render
21807          * Fires after the component is rendered.
21808              * @param {Roo.Component} this
21809              */
21810         render : true,
21811         /**
21812          * @event beforedestroy
21813          * Fires before the component is destroyed. Return false to stop the destroy.
21814              * @param {Roo.Component} this
21815              */
21816         beforedestroy : true,
21817         /**
21818          * @event destroy
21819          * Fires after the component is destroyed.
21820              * @param {Roo.Component} this
21821              */
21822         destroy : true
21823     });
21824     if(!this.id){
21825         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21826     }
21827     Roo.ComponentMgr.register(this);
21828     Roo.Component.superclass.constructor.call(this);
21829     this.initComponent();
21830     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21831         this.render(this.renderTo);
21832         delete this.renderTo;
21833     }
21834 };
21835
21836 // private
21837 Roo.Component.AUTO_ID = 1000;
21838
21839 Roo.extend(Roo.Component, Roo.util.Observable, {
21840     /**
21841      * @property {Boolean} hidden
21842      * true if this component is hidden. Read-only.
21843      */
21844     hidden : false,
21845     /**
21846      * true if this component is disabled. Read-only.
21847      */
21848     disabled : false,
21849     /**
21850      * true if this component has been rendered. Read-only.
21851      */
21852     rendered : false,
21853     
21854     /** @cfg {String} disableClass
21855      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21856      */
21857     disabledClass : "x-item-disabled",
21858         /** @cfg {Boolean} allowDomMove
21859          * Whether the component can move the Dom node when rendering (defaults to true).
21860          */
21861     allowDomMove : true,
21862     /** @cfg {String} hideMode
21863      * How this component should hidden. Supported values are
21864      * "visibility" (css visibility), "offsets" (negative offset position) and
21865      * "display" (css display) - defaults to "display".
21866      */
21867     hideMode: 'display',
21868
21869     // private
21870     ctype : "Roo.Component",
21871
21872     /** @cfg {String} actionMode 
21873      * which property holds the element that used for  hide() / show() / disable() / enable()
21874      * default is 'el' 
21875      */
21876     actionMode : "el",
21877
21878     // private
21879     getActionEl : function(){
21880         return this[this.actionMode];
21881     },
21882
21883     initComponent : Roo.emptyFn,
21884     /**
21885      * If this is a lazy rendering component, render it to its container element.
21886      * @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.
21887      */
21888     render : function(container, position){
21889         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21890             if(!container && this.el){
21891                 this.el = Roo.get(this.el);
21892                 container = this.el.dom.parentNode;
21893                 this.allowDomMove = false;
21894             }
21895             this.container = Roo.get(container);
21896             this.rendered = true;
21897             if(position !== undefined){
21898                 if(typeof position == 'number'){
21899                     position = this.container.dom.childNodes[position];
21900                 }else{
21901                     position = Roo.getDom(position);
21902                 }
21903             }
21904             this.onRender(this.container, position || null);
21905             if(this.cls){
21906                 this.el.addClass(this.cls);
21907                 delete this.cls;
21908             }
21909             if(this.style){
21910                 this.el.applyStyles(this.style);
21911                 delete this.style;
21912             }
21913             this.fireEvent("render", this);
21914             this.afterRender(this.container);
21915             if(this.hidden){
21916                 this.hide();
21917             }
21918             if(this.disabled){
21919                 this.disable();
21920             }
21921         }
21922         return this;
21923     },
21924
21925     // private
21926     // default function is not really useful
21927     onRender : function(ct, position){
21928         if(this.el){
21929             this.el = Roo.get(this.el);
21930             if(this.allowDomMove !== false){
21931                 ct.dom.insertBefore(this.el.dom, position);
21932             }
21933         }
21934     },
21935
21936     // private
21937     getAutoCreate : function(){
21938         var cfg = typeof this.autoCreate == "object" ?
21939                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21940         if(this.id && !cfg.id){
21941             cfg.id = this.id;
21942         }
21943         return cfg;
21944     },
21945
21946     // private
21947     afterRender : Roo.emptyFn,
21948
21949     /**
21950      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21951      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21952      */
21953     destroy : function(){
21954         if(this.fireEvent("beforedestroy", this) !== false){
21955             this.purgeListeners();
21956             this.beforeDestroy();
21957             if(this.rendered){
21958                 this.el.removeAllListeners();
21959                 this.el.remove();
21960                 if(this.actionMode == "container"){
21961                     this.container.remove();
21962                 }
21963             }
21964             this.onDestroy();
21965             Roo.ComponentMgr.unregister(this);
21966             this.fireEvent("destroy", this);
21967         }
21968     },
21969
21970         // private
21971     beforeDestroy : function(){
21972
21973     },
21974
21975         // private
21976         onDestroy : function(){
21977
21978     },
21979
21980     /**
21981      * Returns the underlying {@link Roo.Element}.
21982      * @return {Roo.Element} The element
21983      */
21984     getEl : function(){
21985         return this.el;
21986     },
21987
21988     /**
21989      * Returns the id of this component.
21990      * @return {String}
21991      */
21992     getId : function(){
21993         return this.id;
21994     },
21995
21996     /**
21997      * Try to focus this component.
21998      * @param {Boolean} selectText True to also select the text in this component (if applicable)
21999      * @return {Roo.Component} this
22000      */
22001     focus : function(selectText){
22002         if(this.rendered){
22003             this.el.focus();
22004             if(selectText === true){
22005                 this.el.dom.select();
22006             }
22007         }
22008         return this;
22009     },
22010
22011     // private
22012     blur : function(){
22013         if(this.rendered){
22014             this.el.blur();
22015         }
22016         return this;
22017     },
22018
22019     /**
22020      * Disable this component.
22021      * @return {Roo.Component} this
22022      */
22023     disable : function(){
22024         if(this.rendered){
22025             this.onDisable();
22026         }
22027         this.disabled = true;
22028         this.fireEvent("disable", this);
22029         return this;
22030     },
22031
22032         // private
22033     onDisable : function(){
22034         this.getActionEl().addClass(this.disabledClass);
22035         this.el.dom.disabled = true;
22036     },
22037
22038     /**
22039      * Enable this component.
22040      * @return {Roo.Component} this
22041      */
22042     enable : function(){
22043         if(this.rendered){
22044             this.onEnable();
22045         }
22046         this.disabled = false;
22047         this.fireEvent("enable", this);
22048         return this;
22049     },
22050
22051         // private
22052     onEnable : function(){
22053         this.getActionEl().removeClass(this.disabledClass);
22054         this.el.dom.disabled = false;
22055     },
22056
22057     /**
22058      * Convenience function for setting disabled/enabled by boolean.
22059      * @param {Boolean} disabled
22060      */
22061     setDisabled : function(disabled){
22062         this[disabled ? "disable" : "enable"]();
22063     },
22064
22065     /**
22066      * Show this component.
22067      * @return {Roo.Component} this
22068      */
22069     show: function(){
22070         if(this.fireEvent("beforeshow", this) !== false){
22071             this.hidden = false;
22072             if(this.rendered){
22073                 this.onShow();
22074             }
22075             this.fireEvent("show", this);
22076         }
22077         return this;
22078     },
22079
22080     // private
22081     onShow : function(){
22082         var ae = this.getActionEl();
22083         if(this.hideMode == 'visibility'){
22084             ae.dom.style.visibility = "visible";
22085         }else if(this.hideMode == 'offsets'){
22086             ae.removeClass('x-hidden');
22087         }else{
22088             ae.dom.style.display = "";
22089         }
22090     },
22091
22092     /**
22093      * Hide this component.
22094      * @return {Roo.Component} this
22095      */
22096     hide: function(){
22097         if(this.fireEvent("beforehide", this) !== false){
22098             this.hidden = true;
22099             if(this.rendered){
22100                 this.onHide();
22101             }
22102             this.fireEvent("hide", this);
22103         }
22104         return this;
22105     },
22106
22107     // private
22108     onHide : function(){
22109         var ae = this.getActionEl();
22110         if(this.hideMode == 'visibility'){
22111             ae.dom.style.visibility = "hidden";
22112         }else if(this.hideMode == 'offsets'){
22113             ae.addClass('x-hidden');
22114         }else{
22115             ae.dom.style.display = "none";
22116         }
22117     },
22118
22119     /**
22120      * Convenience function to hide or show this component by boolean.
22121      * @param {Boolean} visible True to show, false to hide
22122      * @return {Roo.Component} this
22123      */
22124     setVisible: function(visible){
22125         if(visible) {
22126             this.show();
22127         }else{
22128             this.hide();
22129         }
22130         return this;
22131     },
22132
22133     /**
22134      * Returns true if this component is visible.
22135      */
22136     isVisible : function(){
22137         return this.getActionEl().isVisible();
22138     },
22139
22140     cloneConfig : function(overrides){
22141         overrides = overrides || {};
22142         var id = overrides.id || Roo.id();
22143         var cfg = Roo.applyIf(overrides, this.initialConfig);
22144         cfg.id = id; // prevent dup id
22145         return new this.constructor(cfg);
22146     }
22147 });/*
22148  * Based on:
22149  * Ext JS Library 1.1.1
22150  * Copyright(c) 2006-2007, Ext JS, LLC.
22151  *
22152  * Originally Released Under LGPL - original licence link has changed is not relivant.
22153  *
22154  * Fork - LGPL
22155  * <script type="text/javascript">
22156  */
22157  (function(){ 
22158 /**
22159  * @class Roo.Layer
22160  * @extends Roo.Element
22161  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22162  * automatic maintaining of shadow/shim positions.
22163  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22164  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22165  * you can pass a string with a CSS class name. False turns off the shadow.
22166  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22167  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22168  * @cfg {String} cls CSS class to add to the element
22169  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22170  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22171  * @constructor
22172  * @param {Object} config An object with config options.
22173  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22174  */
22175
22176 Roo.Layer = function(config, existingEl){
22177     config = config || {};
22178     var dh = Roo.DomHelper;
22179     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22180     if(existingEl){
22181         this.dom = Roo.getDom(existingEl);
22182     }
22183     if(!this.dom){
22184         var o = config.dh || {tag: "div", cls: "x-layer"};
22185         this.dom = dh.append(pel, o);
22186     }
22187     if(config.cls){
22188         this.addClass(config.cls);
22189     }
22190     this.constrain = config.constrain !== false;
22191     this.visibilityMode = Roo.Element.VISIBILITY;
22192     if(config.id){
22193         this.id = this.dom.id = config.id;
22194     }else{
22195         this.id = Roo.id(this.dom);
22196     }
22197     this.zindex = config.zindex || this.getZIndex();
22198     this.position("absolute", this.zindex);
22199     if(config.shadow){
22200         this.shadowOffset = config.shadowOffset || 4;
22201         this.shadow = new Roo.Shadow({
22202             offset : this.shadowOffset,
22203             mode : config.shadow
22204         });
22205     }else{
22206         this.shadowOffset = 0;
22207     }
22208     this.useShim = config.shim !== false && Roo.useShims;
22209     this.useDisplay = config.useDisplay;
22210     this.hide();
22211 };
22212
22213 var supr = Roo.Element.prototype;
22214
22215 // shims are shared among layer to keep from having 100 iframes
22216 var shims = [];
22217
22218 Roo.extend(Roo.Layer, Roo.Element, {
22219
22220     getZIndex : function(){
22221         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22222     },
22223
22224     getShim : function(){
22225         if(!this.useShim){
22226             return null;
22227         }
22228         if(this.shim){
22229             return this.shim;
22230         }
22231         var shim = shims.shift();
22232         if(!shim){
22233             shim = this.createShim();
22234             shim.enableDisplayMode('block');
22235             shim.dom.style.display = 'none';
22236             shim.dom.style.visibility = 'visible';
22237         }
22238         var pn = this.dom.parentNode;
22239         if(shim.dom.parentNode != pn){
22240             pn.insertBefore(shim.dom, this.dom);
22241         }
22242         shim.setStyle('z-index', this.getZIndex()-2);
22243         this.shim = shim;
22244         return shim;
22245     },
22246
22247     hideShim : function(){
22248         if(this.shim){
22249             this.shim.setDisplayed(false);
22250             shims.push(this.shim);
22251             delete this.shim;
22252         }
22253     },
22254
22255     disableShadow : function(){
22256         if(this.shadow){
22257             this.shadowDisabled = true;
22258             this.shadow.hide();
22259             this.lastShadowOffset = this.shadowOffset;
22260             this.shadowOffset = 0;
22261         }
22262     },
22263
22264     enableShadow : function(show){
22265         if(this.shadow){
22266             this.shadowDisabled = false;
22267             this.shadowOffset = this.lastShadowOffset;
22268             delete this.lastShadowOffset;
22269             if(show){
22270                 this.sync(true);
22271             }
22272         }
22273     },
22274
22275     // private
22276     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22277     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22278     sync : function(doShow){
22279         var sw = this.shadow;
22280         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22281             var sh = this.getShim();
22282
22283             var w = this.getWidth(),
22284                 h = this.getHeight();
22285
22286             var l = this.getLeft(true),
22287                 t = this.getTop(true);
22288
22289             if(sw && !this.shadowDisabled){
22290                 if(doShow && !sw.isVisible()){
22291                     sw.show(this);
22292                 }else{
22293                     sw.realign(l, t, w, h);
22294                 }
22295                 if(sh){
22296                     if(doShow){
22297                        sh.show();
22298                     }
22299                     // fit the shim behind the shadow, so it is shimmed too
22300                     var a = sw.adjusts, s = sh.dom.style;
22301                     s.left = (Math.min(l, l+a.l))+"px";
22302                     s.top = (Math.min(t, t+a.t))+"px";
22303                     s.width = (w+a.w)+"px";
22304                     s.height = (h+a.h)+"px";
22305                 }
22306             }else if(sh){
22307                 if(doShow){
22308                    sh.show();
22309                 }
22310                 sh.setSize(w, h);
22311                 sh.setLeftTop(l, t);
22312             }
22313             
22314         }
22315     },
22316
22317     // private
22318     destroy : function(){
22319         this.hideShim();
22320         if(this.shadow){
22321             this.shadow.hide();
22322         }
22323         this.removeAllListeners();
22324         var pn = this.dom.parentNode;
22325         if(pn){
22326             pn.removeChild(this.dom);
22327         }
22328         Roo.Element.uncache(this.id);
22329     },
22330
22331     remove : function(){
22332         this.destroy();
22333     },
22334
22335     // private
22336     beginUpdate : function(){
22337         this.updating = true;
22338     },
22339
22340     // private
22341     endUpdate : function(){
22342         this.updating = false;
22343         this.sync(true);
22344     },
22345
22346     // private
22347     hideUnders : function(negOffset){
22348         if(this.shadow){
22349             this.shadow.hide();
22350         }
22351         this.hideShim();
22352     },
22353
22354     // private
22355     constrainXY : function(){
22356         if(this.constrain){
22357             var vw = Roo.lib.Dom.getViewWidth(),
22358                 vh = Roo.lib.Dom.getViewHeight();
22359             var s = Roo.get(document).getScroll();
22360
22361             var xy = this.getXY();
22362             var x = xy[0], y = xy[1];   
22363             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22364             // only move it if it needs it
22365             var moved = false;
22366             // first validate right/bottom
22367             if((x + w) > vw+s.left){
22368                 x = vw - w - this.shadowOffset;
22369                 moved = true;
22370             }
22371             if((y + h) > vh+s.top){
22372                 y = vh - h - this.shadowOffset;
22373                 moved = true;
22374             }
22375             // then make sure top/left isn't negative
22376             if(x < s.left){
22377                 x = s.left;
22378                 moved = true;
22379             }
22380             if(y < s.top){
22381                 y = s.top;
22382                 moved = true;
22383             }
22384             if(moved){
22385                 if(this.avoidY){
22386                     var ay = this.avoidY;
22387                     if(y <= ay && (y+h) >= ay){
22388                         y = ay-h-5;   
22389                     }
22390                 }
22391                 xy = [x, y];
22392                 this.storeXY(xy);
22393                 supr.setXY.call(this, xy);
22394                 this.sync();
22395             }
22396         }
22397     },
22398
22399     isVisible : function(){
22400         return this.visible;    
22401     },
22402
22403     // private
22404     showAction : function(){
22405         this.visible = true; // track visibility to prevent getStyle calls
22406         if(this.useDisplay === true){
22407             this.setDisplayed("");
22408         }else if(this.lastXY){
22409             supr.setXY.call(this, this.lastXY);
22410         }else if(this.lastLT){
22411             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22412         }
22413     },
22414
22415     // private
22416     hideAction : function(){
22417         this.visible = false;
22418         if(this.useDisplay === true){
22419             this.setDisplayed(false);
22420         }else{
22421             this.setLeftTop(-10000,-10000);
22422         }
22423     },
22424
22425     // overridden Element method
22426     setVisible : function(v, a, d, c, e){
22427         if(v){
22428             this.showAction();
22429         }
22430         if(a && v){
22431             var cb = function(){
22432                 this.sync(true);
22433                 if(c){
22434                     c();
22435                 }
22436             }.createDelegate(this);
22437             supr.setVisible.call(this, true, true, d, cb, e);
22438         }else{
22439             if(!v){
22440                 this.hideUnders(true);
22441             }
22442             var cb = c;
22443             if(a){
22444                 cb = function(){
22445                     this.hideAction();
22446                     if(c){
22447                         c();
22448                     }
22449                 }.createDelegate(this);
22450             }
22451             supr.setVisible.call(this, v, a, d, cb, e);
22452             if(v){
22453                 this.sync(true);
22454             }else if(!a){
22455                 this.hideAction();
22456             }
22457         }
22458     },
22459
22460     storeXY : function(xy){
22461         delete this.lastLT;
22462         this.lastXY = xy;
22463     },
22464
22465     storeLeftTop : function(left, top){
22466         delete this.lastXY;
22467         this.lastLT = [left, top];
22468     },
22469
22470     // private
22471     beforeFx : function(){
22472         this.beforeAction();
22473         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22474     },
22475
22476     // private
22477     afterFx : function(){
22478         Roo.Layer.superclass.afterFx.apply(this, arguments);
22479         this.sync(this.isVisible());
22480     },
22481
22482     // private
22483     beforeAction : function(){
22484         if(!this.updating && this.shadow){
22485             this.shadow.hide();
22486         }
22487     },
22488
22489     // overridden Element method
22490     setLeft : function(left){
22491         this.storeLeftTop(left, this.getTop(true));
22492         supr.setLeft.apply(this, arguments);
22493         this.sync();
22494     },
22495
22496     setTop : function(top){
22497         this.storeLeftTop(this.getLeft(true), top);
22498         supr.setTop.apply(this, arguments);
22499         this.sync();
22500     },
22501
22502     setLeftTop : function(left, top){
22503         this.storeLeftTop(left, top);
22504         supr.setLeftTop.apply(this, arguments);
22505         this.sync();
22506     },
22507
22508     setXY : function(xy, a, d, c, e){
22509         this.fixDisplay();
22510         this.beforeAction();
22511         this.storeXY(xy);
22512         var cb = this.createCB(c);
22513         supr.setXY.call(this, xy, a, d, cb, e);
22514         if(!a){
22515             cb();
22516         }
22517     },
22518
22519     // private
22520     createCB : function(c){
22521         var el = this;
22522         return function(){
22523             el.constrainXY();
22524             el.sync(true);
22525             if(c){
22526                 c();
22527             }
22528         };
22529     },
22530
22531     // overridden Element method
22532     setX : function(x, a, d, c, e){
22533         this.setXY([x, this.getY()], a, d, c, e);
22534     },
22535
22536     // overridden Element method
22537     setY : function(y, a, d, c, e){
22538         this.setXY([this.getX(), y], a, d, c, e);
22539     },
22540
22541     // overridden Element method
22542     setSize : function(w, h, a, d, c, e){
22543         this.beforeAction();
22544         var cb = this.createCB(c);
22545         supr.setSize.call(this, w, h, a, d, cb, e);
22546         if(!a){
22547             cb();
22548         }
22549     },
22550
22551     // overridden Element method
22552     setWidth : function(w, a, d, c, e){
22553         this.beforeAction();
22554         var cb = this.createCB(c);
22555         supr.setWidth.call(this, w, a, d, cb, e);
22556         if(!a){
22557             cb();
22558         }
22559     },
22560
22561     // overridden Element method
22562     setHeight : function(h, a, d, c, e){
22563         this.beforeAction();
22564         var cb = this.createCB(c);
22565         supr.setHeight.call(this, h, a, d, cb, e);
22566         if(!a){
22567             cb();
22568         }
22569     },
22570
22571     // overridden Element method
22572     setBounds : function(x, y, w, h, a, d, c, e){
22573         this.beforeAction();
22574         var cb = this.createCB(c);
22575         if(!a){
22576             this.storeXY([x, y]);
22577             supr.setXY.call(this, [x, y]);
22578             supr.setSize.call(this, w, h, a, d, cb, e);
22579             cb();
22580         }else{
22581             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22582         }
22583         return this;
22584     },
22585     
22586     /**
22587      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22588      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22589      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22590      * @param {Number} zindex The new z-index to set
22591      * @return {this} The Layer
22592      */
22593     setZIndex : function(zindex){
22594         this.zindex = zindex;
22595         this.setStyle("z-index", zindex + 2);
22596         if(this.shadow){
22597             this.shadow.setZIndex(zindex + 1);
22598         }
22599         if(this.shim){
22600             this.shim.setStyle("z-index", zindex);
22601         }
22602     }
22603 });
22604 })();/*
22605  * Based on:
22606  * Ext JS Library 1.1.1
22607  * Copyright(c) 2006-2007, Ext JS, LLC.
22608  *
22609  * Originally Released Under LGPL - original licence link has changed is not relivant.
22610  *
22611  * Fork - LGPL
22612  * <script type="text/javascript">
22613  */
22614
22615
22616 /**
22617  * @class Roo.Shadow
22618  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22619  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22620  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22621  * @constructor
22622  * Create a new Shadow
22623  * @param {Object} config The config object
22624  */
22625 Roo.Shadow = function(config){
22626     Roo.apply(this, config);
22627     if(typeof this.mode != "string"){
22628         this.mode = this.defaultMode;
22629     }
22630     var o = this.offset, a = {h: 0};
22631     var rad = Math.floor(this.offset/2);
22632     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22633         case "drop":
22634             a.w = 0;
22635             a.l = a.t = o;
22636             a.t -= 1;
22637             if(Roo.isIE){
22638                 a.l -= this.offset + rad;
22639                 a.t -= this.offset + rad;
22640                 a.w -= rad;
22641                 a.h -= rad;
22642                 a.t += 1;
22643             }
22644         break;
22645         case "sides":
22646             a.w = (o*2);
22647             a.l = -o;
22648             a.t = o-1;
22649             if(Roo.isIE){
22650                 a.l -= (this.offset - rad);
22651                 a.t -= this.offset + rad;
22652                 a.l += 1;
22653                 a.w -= (this.offset - rad)*2;
22654                 a.w -= rad + 1;
22655                 a.h -= 1;
22656             }
22657         break;
22658         case "frame":
22659             a.w = a.h = (o*2);
22660             a.l = a.t = -o;
22661             a.t += 1;
22662             a.h -= 2;
22663             if(Roo.isIE){
22664                 a.l -= (this.offset - rad);
22665                 a.t -= (this.offset - rad);
22666                 a.l += 1;
22667                 a.w -= (this.offset + rad + 1);
22668                 a.h -= (this.offset + rad);
22669                 a.h += 1;
22670             }
22671         break;
22672     };
22673
22674     this.adjusts = a;
22675 };
22676
22677 Roo.Shadow.prototype = {
22678     /**
22679      * @cfg {String} mode
22680      * The shadow display mode.  Supports the following options:<br />
22681      * sides: Shadow displays on both sides and bottom only<br />
22682      * frame: Shadow displays equally on all four sides<br />
22683      * drop: Traditional bottom-right drop shadow (default)
22684      */
22685     /**
22686      * @cfg {String} offset
22687      * The number of pixels to offset the shadow from the element (defaults to 4)
22688      */
22689     offset: 4,
22690
22691     // private
22692     defaultMode: "drop",
22693
22694     /**
22695      * Displays the shadow under the target element
22696      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22697      */
22698     show : function(target){
22699         target = Roo.get(target);
22700         if(!this.el){
22701             this.el = Roo.Shadow.Pool.pull();
22702             if(this.el.dom.nextSibling != target.dom){
22703                 this.el.insertBefore(target);
22704             }
22705         }
22706         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22707         if(Roo.isIE){
22708             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22709         }
22710         this.realign(
22711             target.getLeft(true),
22712             target.getTop(true),
22713             target.getWidth(),
22714             target.getHeight()
22715         );
22716         this.el.dom.style.display = "block";
22717     },
22718
22719     /**
22720      * Returns true if the shadow is visible, else false
22721      */
22722     isVisible : function(){
22723         return this.el ? true : false;  
22724     },
22725
22726     /**
22727      * Direct alignment when values are already available. Show must be called at least once before
22728      * calling this method to ensure it is initialized.
22729      * @param {Number} left The target element left position
22730      * @param {Number} top The target element top position
22731      * @param {Number} width The target element width
22732      * @param {Number} height The target element height
22733      */
22734     realign : function(l, t, w, h){
22735         if(!this.el){
22736             return;
22737         }
22738         var a = this.adjusts, d = this.el.dom, s = d.style;
22739         var iea = 0;
22740         s.left = (l+a.l)+"px";
22741         s.top = (t+a.t)+"px";
22742         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22743  
22744         if(s.width != sws || s.height != shs){
22745             s.width = sws;
22746             s.height = shs;
22747             if(!Roo.isIE){
22748                 var cn = d.childNodes;
22749                 var sww = Math.max(0, (sw-12))+"px";
22750                 cn[0].childNodes[1].style.width = sww;
22751                 cn[1].childNodes[1].style.width = sww;
22752                 cn[2].childNodes[1].style.width = sww;
22753                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22754             }
22755         }
22756     },
22757
22758     /**
22759      * Hides this shadow
22760      */
22761     hide : function(){
22762         if(this.el){
22763             this.el.dom.style.display = "none";
22764             Roo.Shadow.Pool.push(this.el);
22765             delete this.el;
22766         }
22767     },
22768
22769     /**
22770      * Adjust the z-index of this shadow
22771      * @param {Number} zindex The new z-index
22772      */
22773     setZIndex : function(z){
22774         this.zIndex = z;
22775         if(this.el){
22776             this.el.setStyle("z-index", z);
22777         }
22778     }
22779 };
22780
22781 // Private utility class that manages the internal Shadow cache
22782 Roo.Shadow.Pool = function(){
22783     var p = [];
22784     var markup = Roo.isIE ?
22785                  '<div class="x-ie-shadow"></div>' :
22786                  '<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>';
22787     return {
22788         pull : function(){
22789             var sh = p.shift();
22790             if(!sh){
22791                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22792                 sh.autoBoxAdjust = false;
22793             }
22794             return sh;
22795         },
22796
22797         push : function(sh){
22798             p.push(sh);
22799         }
22800     };
22801 }();/*
22802  * Based on:
22803  * Ext JS Library 1.1.1
22804  * Copyright(c) 2006-2007, Ext JS, LLC.
22805  *
22806  * Originally Released Under LGPL - original licence link has changed is not relivant.
22807  *
22808  * Fork - LGPL
22809  * <script type="text/javascript">
22810  */
22811
22812 /**
22813  * @class Roo.BoxComponent
22814  * @extends Roo.Component
22815  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22816  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22817  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22818  * layout containers.
22819  * @constructor
22820  * @param {Roo.Element/String/Object} config The configuration options.
22821  */
22822 Roo.BoxComponent = function(config){
22823     Roo.Component.call(this, config);
22824     this.addEvents({
22825         /**
22826          * @event resize
22827          * Fires after the component is resized.
22828              * @param {Roo.Component} this
22829              * @param {Number} adjWidth The box-adjusted width that was set
22830              * @param {Number} adjHeight The box-adjusted height that was set
22831              * @param {Number} rawWidth The width that was originally specified
22832              * @param {Number} rawHeight The height that was originally specified
22833              */
22834         resize : true,
22835         /**
22836          * @event move
22837          * Fires after the component is moved.
22838              * @param {Roo.Component} this
22839              * @param {Number} x The new x position
22840              * @param {Number} y The new y position
22841              */
22842         move : true
22843     });
22844 };
22845
22846 Roo.extend(Roo.BoxComponent, Roo.Component, {
22847     // private, set in afterRender to signify that the component has been rendered
22848     boxReady : false,
22849     // private, used to defer height settings to subclasses
22850     deferHeight: false,
22851     /** @cfg {Number} width
22852      * width (optional) size of component
22853      */
22854      /** @cfg {Number} height
22855      * height (optional) size of component
22856      */
22857      
22858     /**
22859      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22860      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22861      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22862      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22863      * @return {Roo.BoxComponent} this
22864      */
22865     setSize : function(w, h){
22866         // support for standard size objects
22867         if(typeof w == 'object'){
22868             h = w.height;
22869             w = w.width;
22870         }
22871         // not rendered
22872         if(!this.boxReady){
22873             this.width = w;
22874             this.height = h;
22875             return this;
22876         }
22877
22878         // prevent recalcs when not needed
22879         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22880             return this;
22881         }
22882         this.lastSize = {width: w, height: h};
22883
22884         var adj = this.adjustSize(w, h);
22885         var aw = adj.width, ah = adj.height;
22886         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22887             var rz = this.getResizeEl();
22888             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22889                 rz.setSize(aw, ah);
22890             }else if(!this.deferHeight && ah !== undefined){
22891                 rz.setHeight(ah);
22892             }else if(aw !== undefined){
22893                 rz.setWidth(aw);
22894             }
22895             this.onResize(aw, ah, w, h);
22896             this.fireEvent('resize', this, aw, ah, w, h);
22897         }
22898         return this;
22899     },
22900
22901     /**
22902      * Gets the current size of the component's underlying element.
22903      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22904      */
22905     getSize : function(){
22906         return this.el.getSize();
22907     },
22908
22909     /**
22910      * Gets the current XY position of the component's underlying element.
22911      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22912      * @return {Array} The XY position of the element (e.g., [100, 200])
22913      */
22914     getPosition : function(local){
22915         if(local === true){
22916             return [this.el.getLeft(true), this.el.getTop(true)];
22917         }
22918         return this.xy || this.el.getXY();
22919     },
22920
22921     /**
22922      * Gets the current box measurements of the component's underlying element.
22923      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22924      * @returns {Object} box An object in the format {x, y, width, height}
22925      */
22926     getBox : function(local){
22927         var s = this.el.getSize();
22928         if(local){
22929             s.x = this.el.getLeft(true);
22930             s.y = this.el.getTop(true);
22931         }else{
22932             var xy = this.xy || this.el.getXY();
22933             s.x = xy[0];
22934             s.y = xy[1];
22935         }
22936         return s;
22937     },
22938
22939     /**
22940      * Sets the current box measurements of the component's underlying element.
22941      * @param {Object} box An object in the format {x, y, width, height}
22942      * @returns {Roo.BoxComponent} this
22943      */
22944     updateBox : function(box){
22945         this.setSize(box.width, box.height);
22946         this.setPagePosition(box.x, box.y);
22947         return this;
22948     },
22949
22950     // protected
22951     getResizeEl : function(){
22952         return this.resizeEl || this.el;
22953     },
22954
22955     // protected
22956     getPositionEl : function(){
22957         return this.positionEl || this.el;
22958     },
22959
22960     /**
22961      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22962      * This method fires the move event.
22963      * @param {Number} left The new left
22964      * @param {Number} top The new top
22965      * @returns {Roo.BoxComponent} this
22966      */
22967     setPosition : function(x, y){
22968         this.x = x;
22969         this.y = y;
22970         if(!this.boxReady){
22971             return this;
22972         }
22973         var adj = this.adjustPosition(x, y);
22974         var ax = adj.x, ay = adj.y;
22975
22976         var el = this.getPositionEl();
22977         if(ax !== undefined || ay !== undefined){
22978             if(ax !== undefined && ay !== undefined){
22979                 el.setLeftTop(ax, ay);
22980             }else if(ax !== undefined){
22981                 el.setLeft(ax);
22982             }else if(ay !== undefined){
22983                 el.setTop(ay);
22984             }
22985             this.onPosition(ax, ay);
22986             this.fireEvent('move', this, ax, ay);
22987         }
22988         return this;
22989     },
22990
22991     /**
22992      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
22993      * This method fires the move event.
22994      * @param {Number} x The new x position
22995      * @param {Number} y The new y position
22996      * @returns {Roo.BoxComponent} this
22997      */
22998     setPagePosition : function(x, y){
22999         this.pageX = x;
23000         this.pageY = y;
23001         if(!this.boxReady){
23002             return;
23003         }
23004         if(x === undefined || y === undefined){ // cannot translate undefined points
23005             return;
23006         }
23007         var p = this.el.translatePoints(x, y);
23008         this.setPosition(p.left, p.top);
23009         return this;
23010     },
23011
23012     // private
23013     onRender : function(ct, position){
23014         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23015         if(this.resizeEl){
23016             this.resizeEl = Roo.get(this.resizeEl);
23017         }
23018         if(this.positionEl){
23019             this.positionEl = Roo.get(this.positionEl);
23020         }
23021     },
23022
23023     // private
23024     afterRender : function(){
23025         Roo.BoxComponent.superclass.afterRender.call(this);
23026         this.boxReady = true;
23027         this.setSize(this.width, this.height);
23028         if(this.x || this.y){
23029             this.setPosition(this.x, this.y);
23030         }
23031         if(this.pageX || this.pageY){
23032             this.setPagePosition(this.pageX, this.pageY);
23033         }
23034     },
23035
23036     /**
23037      * Force the component's size to recalculate based on the underlying element's current height and width.
23038      * @returns {Roo.BoxComponent} this
23039      */
23040     syncSize : function(){
23041         delete this.lastSize;
23042         this.setSize(this.el.getWidth(), this.el.getHeight());
23043         return this;
23044     },
23045
23046     /**
23047      * Called after the component is resized, this method is empty by default but can be implemented by any
23048      * subclass that needs to perform custom logic after a resize occurs.
23049      * @param {Number} adjWidth The box-adjusted width that was set
23050      * @param {Number} adjHeight The box-adjusted height that was set
23051      * @param {Number} rawWidth The width that was originally specified
23052      * @param {Number} rawHeight The height that was originally specified
23053      */
23054     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23055
23056     },
23057
23058     /**
23059      * Called after the component is moved, this method is empty by default but can be implemented by any
23060      * subclass that needs to perform custom logic after a move occurs.
23061      * @param {Number} x The new x position
23062      * @param {Number} y The new y position
23063      */
23064     onPosition : function(x, y){
23065
23066     },
23067
23068     // private
23069     adjustSize : function(w, h){
23070         if(this.autoWidth){
23071             w = 'auto';
23072         }
23073         if(this.autoHeight){
23074             h = 'auto';
23075         }
23076         return {width : w, height: h};
23077     },
23078
23079     // private
23080     adjustPosition : function(x, y){
23081         return {x : x, y: y};
23082     }
23083 });/*
23084  * Based on:
23085  * Ext JS Library 1.1.1
23086  * Copyright(c) 2006-2007, Ext JS, LLC.
23087  *
23088  * Originally Released Under LGPL - original licence link has changed is not relivant.
23089  *
23090  * Fork - LGPL
23091  * <script type="text/javascript">
23092  */
23093
23094
23095 /**
23096  * @class Roo.SplitBar
23097  * @extends Roo.util.Observable
23098  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23099  * <br><br>
23100  * Usage:
23101  * <pre><code>
23102 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23103                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23104 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23105 split.minSize = 100;
23106 split.maxSize = 600;
23107 split.animate = true;
23108 split.on('moved', splitterMoved);
23109 </code></pre>
23110  * @constructor
23111  * Create a new SplitBar
23112  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23113  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23114  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23115  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23116                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23117                         position of the SplitBar).
23118  */
23119 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23120     
23121     /** @private */
23122     this.el = Roo.get(dragElement, true);
23123     this.el.dom.unselectable = "on";
23124     /** @private */
23125     this.resizingEl = Roo.get(resizingElement, true);
23126
23127     /**
23128      * @private
23129      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23130      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23131      * @type Number
23132      */
23133     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23134     
23135     /**
23136      * The minimum size of the resizing element. (Defaults to 0)
23137      * @type Number
23138      */
23139     this.minSize = 0;
23140     
23141     /**
23142      * The maximum size of the resizing element. (Defaults to 2000)
23143      * @type Number
23144      */
23145     this.maxSize = 2000;
23146     
23147     /**
23148      * Whether to animate the transition to the new size
23149      * @type Boolean
23150      */
23151     this.animate = false;
23152     
23153     /**
23154      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23155      * @type Boolean
23156      */
23157     this.useShim = false;
23158     
23159     /** @private */
23160     this.shim = null;
23161     
23162     if(!existingProxy){
23163         /** @private */
23164         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23165     }else{
23166         this.proxy = Roo.get(existingProxy).dom;
23167     }
23168     /** @private */
23169     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23170     
23171     /** @private */
23172     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23173     
23174     /** @private */
23175     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23176     
23177     /** @private */
23178     this.dragSpecs = {};
23179     
23180     /**
23181      * @private The adapter to use to positon and resize elements
23182      */
23183     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23184     this.adapter.init(this);
23185     
23186     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23187         /** @private */
23188         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23189         this.el.addClass("x-splitbar-h");
23190     }else{
23191         /** @private */
23192         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23193         this.el.addClass("x-splitbar-v");
23194     }
23195     
23196     this.addEvents({
23197         /**
23198          * @event resize
23199          * Fires when the splitter is moved (alias for {@link #event-moved})
23200          * @param {Roo.SplitBar} this
23201          * @param {Number} newSize the new width or height
23202          */
23203         "resize" : true,
23204         /**
23205          * @event moved
23206          * Fires when the splitter is moved
23207          * @param {Roo.SplitBar} this
23208          * @param {Number} newSize the new width or height
23209          */
23210         "moved" : true,
23211         /**
23212          * @event beforeresize
23213          * Fires before the splitter is dragged
23214          * @param {Roo.SplitBar} this
23215          */
23216         "beforeresize" : true,
23217
23218         "beforeapply" : true
23219     });
23220
23221     Roo.util.Observable.call(this);
23222 };
23223
23224 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23225     onStartProxyDrag : function(x, y){
23226         this.fireEvent("beforeresize", this);
23227         if(!this.overlay){
23228             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23229             o.unselectable();
23230             o.enableDisplayMode("block");
23231             // all splitbars share the same overlay
23232             Roo.SplitBar.prototype.overlay = o;
23233         }
23234         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23235         this.overlay.show();
23236         Roo.get(this.proxy).setDisplayed("block");
23237         var size = this.adapter.getElementSize(this);
23238         this.activeMinSize = this.getMinimumSize();;
23239         this.activeMaxSize = this.getMaximumSize();;
23240         var c1 = size - this.activeMinSize;
23241         var c2 = Math.max(this.activeMaxSize - size, 0);
23242         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23243             this.dd.resetConstraints();
23244             this.dd.setXConstraint(
23245                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23246                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23247             );
23248             this.dd.setYConstraint(0, 0);
23249         }else{
23250             this.dd.resetConstraints();
23251             this.dd.setXConstraint(0, 0);
23252             this.dd.setYConstraint(
23253                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23254                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23255             );
23256          }
23257         this.dragSpecs.startSize = size;
23258         this.dragSpecs.startPoint = [x, y];
23259         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23260     },
23261     
23262     /** 
23263      * @private Called after the drag operation by the DDProxy
23264      */
23265     onEndProxyDrag : function(e){
23266         Roo.get(this.proxy).setDisplayed(false);
23267         var endPoint = Roo.lib.Event.getXY(e);
23268         if(this.overlay){
23269             this.overlay.hide();
23270         }
23271         var newSize;
23272         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23273             newSize = this.dragSpecs.startSize + 
23274                 (this.placement == Roo.SplitBar.LEFT ?
23275                     endPoint[0] - this.dragSpecs.startPoint[0] :
23276                     this.dragSpecs.startPoint[0] - endPoint[0]
23277                 );
23278         }else{
23279             newSize = this.dragSpecs.startSize + 
23280                 (this.placement == Roo.SplitBar.TOP ?
23281                     endPoint[1] - this.dragSpecs.startPoint[1] :
23282                     this.dragSpecs.startPoint[1] - endPoint[1]
23283                 );
23284         }
23285         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23286         if(newSize != this.dragSpecs.startSize){
23287             if(this.fireEvent('beforeapply', this, newSize) !== false){
23288                 this.adapter.setElementSize(this, newSize);
23289                 this.fireEvent("moved", this, newSize);
23290                 this.fireEvent("resize", this, newSize);
23291             }
23292         }
23293     },
23294     
23295     /**
23296      * Get the adapter this SplitBar uses
23297      * @return The adapter object
23298      */
23299     getAdapter : function(){
23300         return this.adapter;
23301     },
23302     
23303     /**
23304      * Set the adapter this SplitBar uses
23305      * @param {Object} adapter A SplitBar adapter object
23306      */
23307     setAdapter : function(adapter){
23308         this.adapter = adapter;
23309         this.adapter.init(this);
23310     },
23311     
23312     /**
23313      * Gets the minimum size for the resizing element
23314      * @return {Number} The minimum size
23315      */
23316     getMinimumSize : function(){
23317         return this.minSize;
23318     },
23319     
23320     /**
23321      * Sets the minimum size for the resizing element
23322      * @param {Number} minSize The minimum size
23323      */
23324     setMinimumSize : function(minSize){
23325         this.minSize = minSize;
23326     },
23327     
23328     /**
23329      * Gets the maximum size for the resizing element
23330      * @return {Number} The maximum size
23331      */
23332     getMaximumSize : function(){
23333         return this.maxSize;
23334     },
23335     
23336     /**
23337      * Sets the maximum size for the resizing element
23338      * @param {Number} maxSize The maximum size
23339      */
23340     setMaximumSize : function(maxSize){
23341         this.maxSize = maxSize;
23342     },
23343     
23344     /**
23345      * Sets the initialize size for the resizing element
23346      * @param {Number} size The initial size
23347      */
23348     setCurrentSize : function(size){
23349         var oldAnimate = this.animate;
23350         this.animate = false;
23351         this.adapter.setElementSize(this, size);
23352         this.animate = oldAnimate;
23353     },
23354     
23355     /**
23356      * Destroy this splitbar. 
23357      * @param {Boolean} removeEl True to remove the element
23358      */
23359     destroy : function(removeEl){
23360         if(this.shim){
23361             this.shim.remove();
23362         }
23363         this.dd.unreg();
23364         this.proxy.parentNode.removeChild(this.proxy);
23365         if(removeEl){
23366             this.el.remove();
23367         }
23368     }
23369 });
23370
23371 /**
23372  * @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.
23373  */
23374 Roo.SplitBar.createProxy = function(dir){
23375     var proxy = new Roo.Element(document.createElement("div"));
23376     proxy.unselectable();
23377     var cls = 'x-splitbar-proxy';
23378     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23379     document.body.appendChild(proxy.dom);
23380     return proxy.dom;
23381 };
23382
23383 /** 
23384  * @class Roo.SplitBar.BasicLayoutAdapter
23385  * Default Adapter. It assumes the splitter and resizing element are not positioned
23386  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23387  */
23388 Roo.SplitBar.BasicLayoutAdapter = function(){
23389 };
23390
23391 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23392     // do nothing for now
23393     init : function(s){
23394     
23395     },
23396     /**
23397      * Called before drag operations to get the current size of the resizing element. 
23398      * @param {Roo.SplitBar} s The SplitBar using this adapter
23399      */
23400      getElementSize : function(s){
23401         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23402             return s.resizingEl.getWidth();
23403         }else{
23404             return s.resizingEl.getHeight();
23405         }
23406     },
23407     
23408     /**
23409      * Called after drag operations to set the size of the resizing element.
23410      * @param {Roo.SplitBar} s The SplitBar using this adapter
23411      * @param {Number} newSize The new size to set
23412      * @param {Function} onComplete A function to be invoked when resizing is complete
23413      */
23414     setElementSize : function(s, newSize, onComplete){
23415         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23416             if(!s.animate){
23417                 s.resizingEl.setWidth(newSize);
23418                 if(onComplete){
23419                     onComplete(s, newSize);
23420                 }
23421             }else{
23422                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23423             }
23424         }else{
23425             
23426             if(!s.animate){
23427                 s.resizingEl.setHeight(newSize);
23428                 if(onComplete){
23429                     onComplete(s, newSize);
23430                 }
23431             }else{
23432                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23433             }
23434         }
23435     }
23436 };
23437
23438 /** 
23439  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23440  * @extends Roo.SplitBar.BasicLayoutAdapter
23441  * Adapter that  moves the splitter element to align with the resized sizing element. 
23442  * Used with an absolute positioned SplitBar.
23443  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23444  * document.body, make sure you assign an id to the body element.
23445  */
23446 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23447     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23448     this.container = Roo.get(container);
23449 };
23450
23451 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23452     init : function(s){
23453         this.basic.init(s);
23454     },
23455     
23456     getElementSize : function(s){
23457         return this.basic.getElementSize(s);
23458     },
23459     
23460     setElementSize : function(s, newSize, onComplete){
23461         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23462     },
23463     
23464     moveSplitter : function(s){
23465         var yes = Roo.SplitBar;
23466         switch(s.placement){
23467             case yes.LEFT:
23468                 s.el.setX(s.resizingEl.getRight());
23469                 break;
23470             case yes.RIGHT:
23471                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23472                 break;
23473             case yes.TOP:
23474                 s.el.setY(s.resizingEl.getBottom());
23475                 break;
23476             case yes.BOTTOM:
23477                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23478                 break;
23479         }
23480     }
23481 };
23482
23483 /**
23484  * Orientation constant - Create a vertical SplitBar
23485  * @static
23486  * @type Number
23487  */
23488 Roo.SplitBar.VERTICAL = 1;
23489
23490 /**
23491  * Orientation constant - Create a horizontal SplitBar
23492  * @static
23493  * @type Number
23494  */
23495 Roo.SplitBar.HORIZONTAL = 2;
23496
23497 /**
23498  * Placement constant - The resizing element is to the left of the splitter element
23499  * @static
23500  * @type Number
23501  */
23502 Roo.SplitBar.LEFT = 1;
23503
23504 /**
23505  * Placement constant - The resizing element is to the right of the splitter element
23506  * @static
23507  * @type Number
23508  */
23509 Roo.SplitBar.RIGHT = 2;
23510
23511 /**
23512  * Placement constant - The resizing element is positioned above the splitter element
23513  * @static
23514  * @type Number
23515  */
23516 Roo.SplitBar.TOP = 3;
23517
23518 /**
23519  * Placement constant - The resizing element is positioned under splitter element
23520  * @static
23521  * @type Number
23522  */
23523 Roo.SplitBar.BOTTOM = 4;
23524 /*
23525  * Based on:
23526  * Ext JS Library 1.1.1
23527  * Copyright(c) 2006-2007, Ext JS, LLC.
23528  *
23529  * Originally Released Under LGPL - original licence link has changed is not relivant.
23530  *
23531  * Fork - LGPL
23532  * <script type="text/javascript">
23533  */
23534
23535 /**
23536  * @class Roo.View
23537  * @extends Roo.util.Observable
23538  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23539  * This class also supports single and multi selection modes. <br>
23540  * Create a data model bound view:
23541  <pre><code>
23542  var store = new Roo.data.Store(...);
23543
23544  var view = new Roo.View({
23545     el : "my-element",
23546     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23547  
23548     singleSelect: true,
23549     selectedClass: "ydataview-selected",
23550     store: store
23551  });
23552
23553  // listen for node click?
23554  view.on("click", function(vw, index, node, e){
23555  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23556  });
23557
23558  // load XML data
23559  dataModel.load("foobar.xml");
23560  </code></pre>
23561  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23562  * <br><br>
23563  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23564  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23565  * 
23566  * Note: old style constructor is still suported (container, template, config)
23567  * 
23568  * @constructor
23569  * Create a new View
23570  * @param {Object} config The config object
23571  * 
23572  */
23573 Roo.View = function(config, depreciated_tpl, depreciated_config){
23574     
23575     if (typeof(depreciated_tpl) == 'undefined') {
23576         // new way.. - universal constructor.
23577         Roo.apply(this, config);
23578         this.el  = Roo.get(this.el);
23579     } else {
23580         // old format..
23581         this.el  = Roo.get(config);
23582         this.tpl = depreciated_tpl;
23583         Roo.apply(this, depreciated_config);
23584     }
23585      
23586     
23587     if(typeof(this.tpl) == "string"){
23588         this.tpl = new Roo.Template(this.tpl);
23589     } else {
23590         // support xtype ctors..
23591         this.tpl = new Roo.factory(this.tpl, Roo);
23592     }
23593     
23594     
23595     this.tpl.compile();
23596    
23597
23598      
23599     /** @private */
23600     this.addEvents({
23601     /**
23602      * @event beforeclick
23603      * Fires before a click is processed. Returns false to cancel the default action.
23604      * @param {Roo.View} this
23605      * @param {Number} index The index of the target node
23606      * @param {HTMLElement} node The target node
23607      * @param {Roo.EventObject} e The raw event object
23608      */
23609         "beforeclick" : true,
23610     /**
23611      * @event click
23612      * Fires when a template node is clicked.
23613      * @param {Roo.View} this
23614      * @param {Number} index The index of the target node
23615      * @param {HTMLElement} node The target node
23616      * @param {Roo.EventObject} e The raw event object
23617      */
23618         "click" : true,
23619     /**
23620      * @event dblclick
23621      * Fires when a template node is double clicked.
23622      * @param {Roo.View} this
23623      * @param {Number} index The index of the target node
23624      * @param {HTMLElement} node The target node
23625      * @param {Roo.EventObject} e The raw event object
23626      */
23627         "dblclick" : true,
23628     /**
23629      * @event contextmenu
23630      * Fires when a template node is right clicked.
23631      * @param {Roo.View} this
23632      * @param {Number} index The index of the target node
23633      * @param {HTMLElement} node The target node
23634      * @param {Roo.EventObject} e The raw event object
23635      */
23636         "contextmenu" : true,
23637     /**
23638      * @event selectionchange
23639      * Fires when the selected nodes change.
23640      * @param {Roo.View} this
23641      * @param {Array} selections Array of the selected nodes
23642      */
23643         "selectionchange" : true,
23644
23645     /**
23646      * @event beforeselect
23647      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23648      * @param {Roo.View} this
23649      * @param {HTMLElement} node The node to be selected
23650      * @param {Array} selections Array of currently selected nodes
23651      */
23652         "beforeselect" : true
23653     });
23654
23655     this.el.on({
23656         "click": this.onClick,
23657         "dblclick": this.onDblClick,
23658         "contextmenu": this.onContextMenu,
23659         scope:this
23660     });
23661
23662     this.selections = [];
23663     this.nodes = [];
23664     this.cmp = new Roo.CompositeElementLite([]);
23665     if(this.store){
23666         this.store = Roo.factory(this.store, Roo.data);
23667         this.setStore(this.store, true);
23668     }
23669     Roo.View.superclass.constructor.call(this);
23670 };
23671
23672 Roo.extend(Roo.View, Roo.util.Observable, {
23673     
23674      /**
23675      * @cfg {Roo.data.Store} store Data store to load data from.
23676      */
23677     store : false,
23678     
23679     /**
23680      * @cfg {String|Roo.Element} el The container element.
23681      */
23682     el : '',
23683     
23684     /**
23685      * @cfg {String|Roo.Template} tpl The template used by this View 
23686      */
23687     tpl : false,
23688     
23689     /**
23690      * @cfg {String} selectedClass The css class to add to selected nodes
23691      */
23692     selectedClass : "x-view-selected",
23693      /**
23694      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23695      */
23696     emptyText : "",
23697     /**
23698      * @cfg {Boolean} multiSelect Allow multiple selection
23699      */
23700     
23701     multiSelect : false,
23702     /**
23703      * @cfg {Boolean} singleSelect Allow single selection
23704      */
23705     singleSelect:  false,
23706     
23707     /**
23708      * Returns the element this view is bound to.
23709      * @return {Roo.Element}
23710      */
23711     getEl : function(){
23712         return this.el;
23713     },
23714
23715     /**
23716      * Refreshes the view.
23717      */
23718     refresh : function(){
23719         var t = this.tpl;
23720         this.clearSelections();
23721         this.el.update("");
23722         var html = [];
23723         var records = this.store.getRange();
23724         if(records.length < 1){
23725             this.el.update(this.emptyText);
23726             return;
23727         }
23728         for(var i = 0, len = records.length; i < len; i++){
23729             var data = this.prepareData(records[i].data, i, records[i]);
23730             html[html.length] = t.apply(data);
23731         }
23732         this.el.update(html.join(""));
23733         this.nodes = this.el.dom.childNodes;
23734         this.updateIndexes(0);
23735     },
23736
23737     /**
23738      * Function to override to reformat the data that is sent to
23739      * the template for each node.
23740      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23741      * a JSON object for an UpdateManager bound view).
23742      */
23743     prepareData : function(data){
23744         return data;
23745     },
23746
23747     onUpdate : function(ds, record){
23748         this.clearSelections();
23749         var index = this.store.indexOf(record);
23750         var n = this.nodes[index];
23751         this.tpl.insertBefore(n, this.prepareData(record.data));
23752         n.parentNode.removeChild(n);
23753         this.updateIndexes(index, index);
23754     },
23755
23756     onAdd : function(ds, records, index){
23757         this.clearSelections();
23758         if(this.nodes.length == 0){
23759             this.refresh();
23760             return;
23761         }
23762         var n = this.nodes[index];
23763         for(var i = 0, len = records.length; i < len; i++){
23764             var d = this.prepareData(records[i].data);
23765             if(n){
23766                 this.tpl.insertBefore(n, d);
23767             }else{
23768                 this.tpl.append(this.el, d);
23769             }
23770         }
23771         this.updateIndexes(index);
23772     },
23773
23774     onRemove : function(ds, record, index){
23775         this.clearSelections();
23776         this.el.dom.removeChild(this.nodes[index]);
23777         this.updateIndexes(index);
23778     },
23779
23780     /**
23781      * Refresh an individual node.
23782      * @param {Number} index
23783      */
23784     refreshNode : function(index){
23785         this.onUpdate(this.store, this.store.getAt(index));
23786     },
23787
23788     updateIndexes : function(startIndex, endIndex){
23789         var ns = this.nodes;
23790         startIndex = startIndex || 0;
23791         endIndex = endIndex || ns.length - 1;
23792         for(var i = startIndex; i <= endIndex; i++){
23793             ns[i].nodeIndex = i;
23794         }
23795     },
23796
23797     /**
23798      * Changes the data store this view uses and refresh the view.
23799      * @param {Store} store
23800      */
23801     setStore : function(store, initial){
23802         if(!initial && this.store){
23803             this.store.un("datachanged", this.refresh);
23804             this.store.un("add", this.onAdd);
23805             this.store.un("remove", this.onRemove);
23806             this.store.un("update", this.onUpdate);
23807             this.store.un("clear", this.refresh);
23808         }
23809         if(store){
23810           
23811             store.on("datachanged", this.refresh, this);
23812             store.on("add", this.onAdd, this);
23813             store.on("remove", this.onRemove, this);
23814             store.on("update", this.onUpdate, this);
23815             store.on("clear", this.refresh, this);
23816         }
23817         
23818         if(store){
23819             this.refresh();
23820         }
23821     },
23822
23823     /**
23824      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23825      * @param {HTMLElement} node
23826      * @return {HTMLElement} The template node
23827      */
23828     findItemFromChild : function(node){
23829         var el = this.el.dom;
23830         if(!node || node.parentNode == el){
23831                     return node;
23832             }
23833             var p = node.parentNode;
23834             while(p && p != el){
23835             if(p.parentNode == el){
23836                 return p;
23837             }
23838             p = p.parentNode;
23839         }
23840             return null;
23841     },
23842
23843     /** @ignore */
23844     onClick : function(e){
23845         var item = this.findItemFromChild(e.getTarget());
23846         if(item){
23847             var index = this.indexOf(item);
23848             if(this.onItemClick(item, index, e) !== false){
23849                 this.fireEvent("click", this, index, item, e);
23850             }
23851         }else{
23852             this.clearSelections();
23853         }
23854     },
23855
23856     /** @ignore */
23857     onContextMenu : function(e){
23858         var item = this.findItemFromChild(e.getTarget());
23859         if(item){
23860             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23861         }
23862     },
23863
23864     /** @ignore */
23865     onDblClick : function(e){
23866         var item = this.findItemFromChild(e.getTarget());
23867         if(item){
23868             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23869         }
23870     },
23871
23872     onItemClick : function(item, index, e){
23873         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23874             return false;
23875         }
23876         if(this.multiSelect || this.singleSelect){
23877             if(this.multiSelect && e.shiftKey && this.lastSelection){
23878                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23879             }else{
23880                 this.select(item, this.multiSelect && e.ctrlKey);
23881                 this.lastSelection = item;
23882             }
23883             e.preventDefault();
23884         }
23885         return true;
23886     },
23887
23888     /**
23889      * Get the number of selected nodes.
23890      * @return {Number}
23891      */
23892     getSelectionCount : function(){
23893         return this.selections.length;
23894     },
23895
23896     /**
23897      * Get the currently selected nodes.
23898      * @return {Array} An array of HTMLElements
23899      */
23900     getSelectedNodes : function(){
23901         return this.selections;
23902     },
23903
23904     /**
23905      * Get the indexes of the selected nodes.
23906      * @return {Array}
23907      */
23908     getSelectedIndexes : function(){
23909         var indexes = [], s = this.selections;
23910         for(var i = 0, len = s.length; i < len; i++){
23911             indexes.push(s[i].nodeIndex);
23912         }
23913         return indexes;
23914     },
23915
23916     /**
23917      * Clear all selections
23918      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23919      */
23920     clearSelections : function(suppressEvent){
23921         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23922             this.cmp.elements = this.selections;
23923             this.cmp.removeClass(this.selectedClass);
23924             this.selections = [];
23925             if(!suppressEvent){
23926                 this.fireEvent("selectionchange", this, this.selections);
23927             }
23928         }
23929     },
23930
23931     /**
23932      * Returns true if the passed node is selected
23933      * @param {HTMLElement/Number} node The node or node index
23934      * @return {Boolean}
23935      */
23936     isSelected : function(node){
23937         var s = this.selections;
23938         if(s.length < 1){
23939             return false;
23940         }
23941         node = this.getNode(node);
23942         return s.indexOf(node) !== -1;
23943     },
23944
23945     /**
23946      * Selects nodes.
23947      * @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
23948      * @param {Boolean} keepExisting (optional) true to keep existing selections
23949      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23950      */
23951     select : function(nodeInfo, keepExisting, suppressEvent){
23952         if(nodeInfo instanceof Array){
23953             if(!keepExisting){
23954                 this.clearSelections(true);
23955             }
23956             for(var i = 0, len = nodeInfo.length; i < len; i++){
23957                 this.select(nodeInfo[i], true, true);
23958             }
23959         } else{
23960             var node = this.getNode(nodeInfo);
23961             if(node && !this.isSelected(node)){
23962                 if(!keepExisting){
23963                     this.clearSelections(true);
23964                 }
23965                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23966                     Roo.fly(node).addClass(this.selectedClass);
23967                     this.selections.push(node);
23968                     if(!suppressEvent){
23969                         this.fireEvent("selectionchange", this, this.selections);
23970                     }
23971                 }
23972             }
23973         }
23974     },
23975
23976     /**
23977      * Gets a template node.
23978      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23979      * @return {HTMLElement} The node or null if it wasn't found
23980      */
23981     getNode : function(nodeInfo){
23982         if(typeof nodeInfo == "string"){
23983             return document.getElementById(nodeInfo);
23984         }else if(typeof nodeInfo == "number"){
23985             return this.nodes[nodeInfo];
23986         }
23987         return nodeInfo;
23988     },
23989
23990     /**
23991      * Gets a range template nodes.
23992      * @param {Number} startIndex
23993      * @param {Number} endIndex
23994      * @return {Array} An array of nodes
23995      */
23996     getNodes : function(start, end){
23997         var ns = this.nodes;
23998         start = start || 0;
23999         end = typeof end == "undefined" ? ns.length - 1 : end;
24000         var nodes = [];
24001         if(start <= end){
24002             for(var i = start; i <= end; i++){
24003                 nodes.push(ns[i]);
24004             }
24005         } else{
24006             for(var i = start; i >= end; i--){
24007                 nodes.push(ns[i]);
24008             }
24009         }
24010         return nodes;
24011     },
24012
24013     /**
24014      * Finds the index of the passed node
24015      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24016      * @return {Number} The index of the node or -1
24017      */
24018     indexOf : function(node){
24019         node = this.getNode(node);
24020         if(typeof node.nodeIndex == "number"){
24021             return node.nodeIndex;
24022         }
24023         var ns = this.nodes;
24024         for(var i = 0, len = ns.length; i < len; i++){
24025             if(ns[i] == node){
24026                 return i;
24027             }
24028         }
24029         return -1;
24030     }
24031 });
24032 /*
24033  * Based on:
24034  * Ext JS Library 1.1.1
24035  * Copyright(c) 2006-2007, Ext JS, LLC.
24036  *
24037  * Originally Released Under LGPL - original licence link has changed is not relivant.
24038  *
24039  * Fork - LGPL
24040  * <script type="text/javascript">
24041  */
24042
24043 /**
24044  * @class Roo.JsonView
24045  * @extends Roo.View
24046  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24047 <pre><code>
24048 var view = new Roo.JsonView({
24049     container: "my-element",
24050     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24051     multiSelect: true, 
24052     jsonRoot: "data" 
24053 });
24054
24055 // listen for node click?
24056 view.on("click", function(vw, index, node, e){
24057     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24058 });
24059
24060 // direct load of JSON data
24061 view.load("foobar.php");
24062
24063 // Example from my blog list
24064 var tpl = new Roo.Template(
24065     '&lt;div class="entry"&gt;' +
24066     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24067     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24068     "&lt;/div&gt;&lt;hr /&gt;"
24069 );
24070
24071 var moreView = new Roo.JsonView({
24072     container :  "entry-list", 
24073     template : tpl,
24074     jsonRoot: "posts"
24075 });
24076 moreView.on("beforerender", this.sortEntries, this);
24077 moreView.load({
24078     url: "/blog/get-posts.php",
24079     params: "allposts=true",
24080     text: "Loading Blog Entries..."
24081 });
24082 </code></pre>
24083
24084 * Note: old code is supported with arguments : (container, template, config)
24085
24086
24087  * @constructor
24088  * Create a new JsonView
24089  * 
24090  * @param {Object} config The config object
24091  * 
24092  */
24093 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24094     
24095     
24096     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24097
24098     var um = this.el.getUpdateManager();
24099     um.setRenderer(this);
24100     um.on("update", this.onLoad, this);
24101     um.on("failure", this.onLoadException, this);
24102
24103     /**
24104      * @event beforerender
24105      * Fires before rendering of the downloaded JSON data.
24106      * @param {Roo.JsonView} this
24107      * @param {Object} data The JSON data loaded
24108      */
24109     /**
24110      * @event load
24111      * Fires when data is loaded.
24112      * @param {Roo.JsonView} this
24113      * @param {Object} data The JSON data loaded
24114      * @param {Object} response The raw Connect response object
24115      */
24116     /**
24117      * @event loadexception
24118      * Fires when loading fails.
24119      * @param {Roo.JsonView} this
24120      * @param {Object} response The raw Connect response object
24121      */
24122     this.addEvents({
24123         'beforerender' : true,
24124         'load' : true,
24125         'loadexception' : true
24126     });
24127 };
24128 Roo.extend(Roo.JsonView, Roo.View, {
24129     /**
24130      * @type {String} The root property in the loaded JSON object that contains the data
24131      */
24132     jsonRoot : "",
24133
24134     /**
24135      * Refreshes the view.
24136      */
24137     refresh : function(){
24138         this.clearSelections();
24139         this.el.update("");
24140         var html = [];
24141         var o = this.jsonData;
24142         if(o && o.length > 0){
24143             for(var i = 0, len = o.length; i < len; i++){
24144                 var data = this.prepareData(o[i], i, o);
24145                 html[html.length] = this.tpl.apply(data);
24146             }
24147         }else{
24148             html.push(this.emptyText);
24149         }
24150         this.el.update(html.join(""));
24151         this.nodes = this.el.dom.childNodes;
24152         this.updateIndexes(0);
24153     },
24154
24155     /**
24156      * 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.
24157      * @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:
24158      <pre><code>
24159      view.load({
24160          url: "your-url.php",
24161          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24162          callback: yourFunction,
24163          scope: yourObject, //(optional scope)
24164          discardUrl: false,
24165          nocache: false,
24166          text: "Loading...",
24167          timeout: 30,
24168          scripts: false
24169      });
24170      </code></pre>
24171      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24172      * 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.
24173      * @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}
24174      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24175      * @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.
24176      */
24177     load : function(){
24178         var um = this.el.getUpdateManager();
24179         um.update.apply(um, arguments);
24180     },
24181
24182     render : function(el, response){
24183         this.clearSelections();
24184         this.el.update("");
24185         var o;
24186         try{
24187             o = Roo.util.JSON.decode(response.responseText);
24188             if(this.jsonRoot){
24189                 
24190                 o = o[this.jsonRoot];
24191             }
24192         } catch(e){
24193         }
24194         /**
24195          * The current JSON data or null
24196          */
24197         this.jsonData = o;
24198         this.beforeRender();
24199         this.refresh();
24200     },
24201
24202 /**
24203  * Get the number of records in the current JSON dataset
24204  * @return {Number}
24205  */
24206     getCount : function(){
24207         return this.jsonData ? this.jsonData.length : 0;
24208     },
24209
24210 /**
24211  * Returns the JSON object for the specified node(s)
24212  * @param {HTMLElement/Array} node The node or an array of nodes
24213  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24214  * you get the JSON object for the node
24215  */
24216     getNodeData : function(node){
24217         if(node instanceof Array){
24218             var data = [];
24219             for(var i = 0, len = node.length; i < len; i++){
24220                 data.push(this.getNodeData(node[i]));
24221             }
24222             return data;
24223         }
24224         return this.jsonData[this.indexOf(node)] || null;
24225     },
24226
24227     beforeRender : function(){
24228         this.snapshot = this.jsonData;
24229         if(this.sortInfo){
24230             this.sort.apply(this, this.sortInfo);
24231         }
24232         this.fireEvent("beforerender", this, this.jsonData);
24233     },
24234
24235     onLoad : function(el, o){
24236         this.fireEvent("load", this, this.jsonData, o);
24237     },
24238
24239     onLoadException : function(el, o){
24240         this.fireEvent("loadexception", this, o);
24241     },
24242
24243 /**
24244  * Filter the data by a specific property.
24245  * @param {String} property A property on your JSON objects
24246  * @param {String/RegExp} value Either string that the property values
24247  * should start with, or a RegExp to test against the property
24248  */
24249     filter : function(property, value){
24250         if(this.jsonData){
24251             var data = [];
24252             var ss = this.snapshot;
24253             if(typeof value == "string"){
24254                 var vlen = value.length;
24255                 if(vlen == 0){
24256                     this.clearFilter();
24257                     return;
24258                 }
24259                 value = value.toLowerCase();
24260                 for(var i = 0, len = ss.length; i < len; i++){
24261                     var o = ss[i];
24262                     if(o[property].substr(0, vlen).toLowerCase() == value){
24263                         data.push(o);
24264                     }
24265                 }
24266             } else if(value.exec){ // regex?
24267                 for(var i = 0, len = ss.length; i < len; i++){
24268                     var o = ss[i];
24269                     if(value.test(o[property])){
24270                         data.push(o);
24271                     }
24272                 }
24273             } else{
24274                 return;
24275             }
24276             this.jsonData = data;
24277             this.refresh();
24278         }
24279     },
24280
24281 /**
24282  * Filter by a function. The passed function will be called with each
24283  * object in the current dataset. If the function returns true the value is kept,
24284  * otherwise it is filtered.
24285  * @param {Function} fn
24286  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24287  */
24288     filterBy : function(fn, scope){
24289         if(this.jsonData){
24290             var data = [];
24291             var ss = this.snapshot;
24292             for(var i = 0, len = ss.length; i < len; i++){
24293                 var o = ss[i];
24294                 if(fn.call(scope || this, o)){
24295                     data.push(o);
24296                 }
24297             }
24298             this.jsonData = data;
24299             this.refresh();
24300         }
24301     },
24302
24303 /**
24304  * Clears the current filter.
24305  */
24306     clearFilter : function(){
24307         if(this.snapshot && this.jsonData != this.snapshot){
24308             this.jsonData = this.snapshot;
24309             this.refresh();
24310         }
24311     },
24312
24313
24314 /**
24315  * Sorts the data for this view and refreshes it.
24316  * @param {String} property A property on your JSON objects to sort on
24317  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24318  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24319  */
24320     sort : function(property, dir, sortType){
24321         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24322         if(this.jsonData){
24323             var p = property;
24324             var dsc = dir && dir.toLowerCase() == "desc";
24325             var f = function(o1, o2){
24326                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24327                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24328                 ;
24329                 if(v1 < v2){
24330                     return dsc ? +1 : -1;
24331                 } else if(v1 > v2){
24332                     return dsc ? -1 : +1;
24333                 } else{
24334                     return 0;
24335                 }
24336             };
24337             this.jsonData.sort(f);
24338             this.refresh();
24339             if(this.jsonData != this.snapshot){
24340                 this.snapshot.sort(f);
24341             }
24342         }
24343     }
24344 });/*
24345  * Based on:
24346  * Ext JS Library 1.1.1
24347  * Copyright(c) 2006-2007, Ext JS, LLC.
24348  *
24349  * Originally Released Under LGPL - original licence link has changed is not relivant.
24350  *
24351  * Fork - LGPL
24352  * <script type="text/javascript">
24353  */
24354  
24355
24356 /**
24357  * @class Roo.ColorPalette
24358  * @extends Roo.Component
24359  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24360  * Here's an example of typical usage:
24361  * <pre><code>
24362 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24363 cp.render('my-div');
24364
24365 cp.on('select', function(palette, selColor){
24366     // do something with selColor
24367 });
24368 </code></pre>
24369  * @constructor
24370  * Create a new ColorPalette
24371  * @param {Object} config The config object
24372  */
24373 Roo.ColorPalette = function(config){
24374     Roo.ColorPalette.superclass.constructor.call(this, config);
24375     this.addEvents({
24376         /**
24377              * @event select
24378              * Fires when a color is selected
24379              * @param {ColorPalette} this
24380              * @param {String} color The 6-digit color hex code (without the # symbol)
24381              */
24382         select: true
24383     });
24384
24385     if(this.handler){
24386         this.on("select", this.handler, this.scope, true);
24387     }
24388 };
24389 Roo.extend(Roo.ColorPalette, Roo.Component, {
24390     /**
24391      * @cfg {String} itemCls
24392      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24393      */
24394     itemCls : "x-color-palette",
24395     /**
24396      * @cfg {String} value
24397      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24398      * the hex codes are case-sensitive.
24399      */
24400     value : null,
24401     clickEvent:'click',
24402     // private
24403     ctype: "Roo.ColorPalette",
24404
24405     /**
24406      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24407      */
24408     allowReselect : false,
24409
24410     /**
24411      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24412      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24413      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24414      * of colors with the width setting until the box is symmetrical.</p>
24415      * <p>You can override individual colors if needed:</p>
24416      * <pre><code>
24417 var cp = new Roo.ColorPalette();
24418 cp.colors[0] = "FF0000";  // change the first box to red
24419 </code></pre>
24420
24421 Or you can provide a custom array of your own for complete control:
24422 <pre><code>
24423 var cp = new Roo.ColorPalette();
24424 cp.colors = ["000000", "993300", "333300"];
24425 </code></pre>
24426      * @type Array
24427      */
24428     colors : [
24429         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24430         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24431         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24432         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24433         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24434     ],
24435
24436     // private
24437     onRender : function(container, position){
24438         var t = new Roo.MasterTemplate(
24439             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24440         );
24441         var c = this.colors;
24442         for(var i = 0, len = c.length; i < len; i++){
24443             t.add([c[i]]);
24444         }
24445         var el = document.createElement("div");
24446         el.className = this.itemCls;
24447         t.overwrite(el);
24448         container.dom.insertBefore(el, position);
24449         this.el = Roo.get(el);
24450         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24451         if(this.clickEvent != 'click'){
24452             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24453         }
24454     },
24455
24456     // private
24457     afterRender : function(){
24458         Roo.ColorPalette.superclass.afterRender.call(this);
24459         if(this.value){
24460             var s = this.value;
24461             this.value = null;
24462             this.select(s);
24463         }
24464     },
24465
24466     // private
24467     handleClick : function(e, t){
24468         e.preventDefault();
24469         if(!this.disabled){
24470             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24471             this.select(c.toUpperCase());
24472         }
24473     },
24474
24475     /**
24476      * Selects the specified color in the palette (fires the select event)
24477      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24478      */
24479     select : function(color){
24480         color = color.replace("#", "");
24481         if(color != this.value || this.allowReselect){
24482             var el = this.el;
24483             if(this.value){
24484                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24485             }
24486             el.child("a.color-"+color).addClass("x-color-palette-sel");
24487             this.value = color;
24488             this.fireEvent("select", this, color);
24489         }
24490     }
24491 });/*
24492  * Based on:
24493  * Ext JS Library 1.1.1
24494  * Copyright(c) 2006-2007, Ext JS, LLC.
24495  *
24496  * Originally Released Under LGPL - original licence link has changed is not relivant.
24497  *
24498  * Fork - LGPL
24499  * <script type="text/javascript">
24500  */
24501  
24502 /**
24503  * @class Roo.DatePicker
24504  * @extends Roo.Component
24505  * Simple date picker class.
24506  * @constructor
24507  * Create a new DatePicker
24508  * @param {Object} config The config object
24509  */
24510 Roo.DatePicker = function(config){
24511     Roo.DatePicker.superclass.constructor.call(this, config);
24512
24513     this.value = config && config.value ?
24514                  config.value.clearTime() : new Date().clearTime();
24515
24516     this.addEvents({
24517         /**
24518              * @event select
24519              * Fires when a date is selected
24520              * @param {DatePicker} this
24521              * @param {Date} date The selected date
24522              */
24523         select: true
24524     });
24525
24526     if(this.handler){
24527         this.on("select", this.handler,  this.scope || this);
24528     }
24529     // build the disabledDatesRE
24530     if(!this.disabledDatesRE && this.disabledDates){
24531         var dd = this.disabledDates;
24532         var re = "(?:";
24533         for(var i = 0; i < dd.length; i++){
24534             re += dd[i];
24535             if(i != dd.length-1) re += "|";
24536         }
24537         this.disabledDatesRE = new RegExp(re + ")");
24538     }
24539 };
24540
24541 Roo.extend(Roo.DatePicker, Roo.Component, {
24542     /**
24543      * @cfg {String} todayText
24544      * The text to display on the button that selects the current date (defaults to "Today")
24545      */
24546     todayText : "Today",
24547     /**
24548      * @cfg {String} okText
24549      * The text to display on the ok button
24550      */
24551     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24552     /**
24553      * @cfg {String} cancelText
24554      * The text to display on the cancel button
24555      */
24556     cancelText : "Cancel",
24557     /**
24558      * @cfg {String} todayTip
24559      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24560      */
24561     todayTip : "{0} (Spacebar)",
24562     /**
24563      * @cfg {Date} minDate
24564      * Minimum allowable date (JavaScript date object, defaults to null)
24565      */
24566     minDate : null,
24567     /**
24568      * @cfg {Date} maxDate
24569      * Maximum allowable date (JavaScript date object, defaults to null)
24570      */
24571     maxDate : null,
24572     /**
24573      * @cfg {String} minText
24574      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24575      */
24576     minText : "This date is before the minimum date",
24577     /**
24578      * @cfg {String} maxText
24579      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24580      */
24581     maxText : "This date is after the maximum date",
24582     /**
24583      * @cfg {String} format
24584      * The default date format string which can be overriden for localization support.  The format must be
24585      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24586      */
24587     format : "m/d/y",
24588     /**
24589      * @cfg {Array} disabledDays
24590      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24591      */
24592     disabledDays : null,
24593     /**
24594      * @cfg {String} disabledDaysText
24595      * The tooltip to display when the date falls on a disabled day (defaults to "")
24596      */
24597     disabledDaysText : "",
24598     /**
24599      * @cfg {RegExp} disabledDatesRE
24600      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24601      */
24602     disabledDatesRE : null,
24603     /**
24604      * @cfg {String} disabledDatesText
24605      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24606      */
24607     disabledDatesText : "",
24608     /**
24609      * @cfg {Boolean} constrainToViewport
24610      * True to constrain the date picker to the viewport (defaults to true)
24611      */
24612     constrainToViewport : true,
24613     /**
24614      * @cfg {Array} monthNames
24615      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24616      */
24617     monthNames : Date.monthNames,
24618     /**
24619      * @cfg {Array} dayNames
24620      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24621      */
24622     dayNames : Date.dayNames,
24623     /**
24624      * @cfg {String} nextText
24625      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24626      */
24627     nextText: 'Next Month (Control+Right)',
24628     /**
24629      * @cfg {String} prevText
24630      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24631      */
24632     prevText: 'Previous Month (Control+Left)',
24633     /**
24634      * @cfg {String} monthYearText
24635      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24636      */
24637     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24638     /**
24639      * @cfg {Number} startDay
24640      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24641      */
24642     startDay : 0,
24643     /**
24644      * @cfg {Bool} showClear
24645      * Show a clear button (usefull for date form elements that can be blank.)
24646      */
24647     
24648     showClear: false,
24649     
24650     /**
24651      * Sets the value of the date field
24652      * @param {Date} value The date to set
24653      */
24654     setValue : function(value){
24655         var old = this.value;
24656         this.value = value.clearTime(true);
24657         if(this.el){
24658             this.update(this.value);
24659         }
24660     },
24661
24662     /**
24663      * Gets the current selected value of the date field
24664      * @return {Date} The selected date
24665      */
24666     getValue : function(){
24667         return this.value;
24668     },
24669
24670     // private
24671     focus : function(){
24672         if(this.el){
24673             this.update(this.activeDate);
24674         }
24675     },
24676
24677     // private
24678     onRender : function(container, position){
24679         var m = [
24680              '<table cellspacing="0">',
24681                 '<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>',
24682                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24683         var dn = this.dayNames;
24684         for(var i = 0; i < 7; i++){
24685             var d = this.startDay+i;
24686             if(d > 6){
24687                 d = d-7;
24688             }
24689             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24690         }
24691         m[m.length] = "</tr></thead><tbody><tr>";
24692         for(var i = 0; i < 42; i++) {
24693             if(i % 7 == 0 && i != 0){
24694                 m[m.length] = "</tr><tr>";
24695             }
24696             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24697         }
24698         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24699             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24700
24701         var el = document.createElement("div");
24702         el.className = "x-date-picker";
24703         el.innerHTML = m.join("");
24704
24705         container.dom.insertBefore(el, position);
24706
24707         this.el = Roo.get(el);
24708         this.eventEl = Roo.get(el.firstChild);
24709
24710         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24711             handler: this.showPrevMonth,
24712             scope: this,
24713             preventDefault:true,
24714             stopDefault:true
24715         });
24716
24717         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24718             handler: this.showNextMonth,
24719             scope: this,
24720             preventDefault:true,
24721             stopDefault:true
24722         });
24723
24724         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24725
24726         this.monthPicker = this.el.down('div.x-date-mp');
24727         this.monthPicker.enableDisplayMode('block');
24728         
24729         var kn = new Roo.KeyNav(this.eventEl, {
24730             "left" : function(e){
24731                 e.ctrlKey ?
24732                     this.showPrevMonth() :
24733                     this.update(this.activeDate.add("d", -1));
24734             },
24735
24736             "right" : function(e){
24737                 e.ctrlKey ?
24738                     this.showNextMonth() :
24739                     this.update(this.activeDate.add("d", 1));
24740             },
24741
24742             "up" : function(e){
24743                 e.ctrlKey ?
24744                     this.showNextYear() :
24745                     this.update(this.activeDate.add("d", -7));
24746             },
24747
24748             "down" : function(e){
24749                 e.ctrlKey ?
24750                     this.showPrevYear() :
24751                     this.update(this.activeDate.add("d", 7));
24752             },
24753
24754             "pageUp" : function(e){
24755                 this.showNextMonth();
24756             },
24757
24758             "pageDown" : function(e){
24759                 this.showPrevMonth();
24760             },
24761
24762             "enter" : function(e){
24763                 e.stopPropagation();
24764                 return true;
24765             },
24766
24767             scope : this
24768         });
24769
24770         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24771
24772         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24773
24774         this.el.unselectable();
24775         
24776         this.cells = this.el.select("table.x-date-inner tbody td");
24777         this.textNodes = this.el.query("table.x-date-inner tbody span");
24778
24779         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24780             text: "&#160;",
24781             tooltip: this.monthYearText
24782         });
24783
24784         this.mbtn.on('click', this.showMonthPicker, this);
24785         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24786
24787
24788         var today = (new Date()).dateFormat(this.format);
24789         
24790         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24791         if (this.showClear) {
24792             baseTb.add( new Roo.Toolbar.Fill());
24793         }
24794         baseTb.add({
24795             text: String.format(this.todayText, today),
24796             tooltip: String.format(this.todayTip, today),
24797             handler: this.selectToday,
24798             scope: this
24799         });
24800         
24801         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24802             
24803         //});
24804         if (this.showClear) {
24805             
24806             baseTb.add( new Roo.Toolbar.Fill());
24807             baseTb.add({
24808                 text: '&#160;',
24809                 cls: 'x-btn-icon x-btn-clear',
24810                 handler: function() {
24811                     //this.value = '';
24812                     this.fireEvent("select", this, '');
24813                 },
24814                 scope: this
24815             });
24816         }
24817         
24818         
24819         if(Roo.isIE){
24820             this.el.repaint();
24821         }
24822         this.update(this.value);
24823     },
24824
24825     createMonthPicker : function(){
24826         if(!this.monthPicker.dom.firstChild){
24827             var buf = ['<table border="0" cellspacing="0">'];
24828             for(var i = 0; i < 6; i++){
24829                 buf.push(
24830                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24831                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24832                     i == 0 ?
24833                     '<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>' :
24834                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24835                 );
24836             }
24837             buf.push(
24838                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24839                     this.okText,
24840                     '</button><button type="button" class="x-date-mp-cancel">',
24841                     this.cancelText,
24842                     '</button></td></tr>',
24843                 '</table>'
24844             );
24845             this.monthPicker.update(buf.join(''));
24846             this.monthPicker.on('click', this.onMonthClick, this);
24847             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24848
24849             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24850             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24851
24852             this.mpMonths.each(function(m, a, i){
24853                 i += 1;
24854                 if((i%2) == 0){
24855                     m.dom.xmonth = 5 + Math.round(i * .5);
24856                 }else{
24857                     m.dom.xmonth = Math.round((i-1) * .5);
24858                 }
24859             });
24860         }
24861     },
24862
24863     showMonthPicker : function(){
24864         this.createMonthPicker();
24865         var size = this.el.getSize();
24866         this.monthPicker.setSize(size);
24867         this.monthPicker.child('table').setSize(size);
24868
24869         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24870         this.updateMPMonth(this.mpSelMonth);
24871         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24872         this.updateMPYear(this.mpSelYear);
24873
24874         this.monthPicker.slideIn('t', {duration:.2});
24875     },
24876
24877     updateMPYear : function(y){
24878         this.mpyear = y;
24879         var ys = this.mpYears.elements;
24880         for(var i = 1; i <= 10; i++){
24881             var td = ys[i-1], y2;
24882             if((i%2) == 0){
24883                 y2 = y + Math.round(i * .5);
24884                 td.firstChild.innerHTML = y2;
24885                 td.xyear = y2;
24886             }else{
24887                 y2 = y - (5-Math.round(i * .5));
24888                 td.firstChild.innerHTML = y2;
24889                 td.xyear = y2;
24890             }
24891             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24892         }
24893     },
24894
24895     updateMPMonth : function(sm){
24896         this.mpMonths.each(function(m, a, i){
24897             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24898         });
24899     },
24900
24901     selectMPMonth: function(m){
24902         
24903     },
24904
24905     onMonthClick : function(e, t){
24906         e.stopEvent();
24907         var el = new Roo.Element(t), pn;
24908         if(el.is('button.x-date-mp-cancel')){
24909             this.hideMonthPicker();
24910         }
24911         else if(el.is('button.x-date-mp-ok')){
24912             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24913             this.hideMonthPicker();
24914         }
24915         else if(pn = el.up('td.x-date-mp-month', 2)){
24916             this.mpMonths.removeClass('x-date-mp-sel');
24917             pn.addClass('x-date-mp-sel');
24918             this.mpSelMonth = pn.dom.xmonth;
24919         }
24920         else if(pn = el.up('td.x-date-mp-year', 2)){
24921             this.mpYears.removeClass('x-date-mp-sel');
24922             pn.addClass('x-date-mp-sel');
24923             this.mpSelYear = pn.dom.xyear;
24924         }
24925         else if(el.is('a.x-date-mp-prev')){
24926             this.updateMPYear(this.mpyear-10);
24927         }
24928         else if(el.is('a.x-date-mp-next')){
24929             this.updateMPYear(this.mpyear+10);
24930         }
24931     },
24932
24933     onMonthDblClick : function(e, t){
24934         e.stopEvent();
24935         var el = new Roo.Element(t), pn;
24936         if(pn = el.up('td.x-date-mp-month', 2)){
24937             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24938             this.hideMonthPicker();
24939         }
24940         else if(pn = el.up('td.x-date-mp-year', 2)){
24941             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24942             this.hideMonthPicker();
24943         }
24944     },
24945
24946     hideMonthPicker : function(disableAnim){
24947         if(this.monthPicker){
24948             if(disableAnim === true){
24949                 this.monthPicker.hide();
24950             }else{
24951                 this.monthPicker.slideOut('t', {duration:.2});
24952             }
24953         }
24954     },
24955
24956     // private
24957     showPrevMonth : function(e){
24958         this.update(this.activeDate.add("mo", -1));
24959     },
24960
24961     // private
24962     showNextMonth : function(e){
24963         this.update(this.activeDate.add("mo", 1));
24964     },
24965
24966     // private
24967     showPrevYear : function(){
24968         this.update(this.activeDate.add("y", -1));
24969     },
24970
24971     // private
24972     showNextYear : function(){
24973         this.update(this.activeDate.add("y", 1));
24974     },
24975
24976     // private
24977     handleMouseWheel : function(e){
24978         var delta = e.getWheelDelta();
24979         if(delta > 0){
24980             this.showPrevMonth();
24981             e.stopEvent();
24982         } else if(delta < 0){
24983             this.showNextMonth();
24984             e.stopEvent();
24985         }
24986     },
24987
24988     // private
24989     handleDateClick : function(e, t){
24990         e.stopEvent();
24991         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
24992             this.setValue(new Date(t.dateValue));
24993             this.fireEvent("select", this, this.value);
24994         }
24995     },
24996
24997     // private
24998     selectToday : function(){
24999         this.setValue(new Date().clearTime());
25000         this.fireEvent("select", this, this.value);
25001     },
25002
25003     // private
25004     update : function(date){
25005         var vd = this.activeDate;
25006         this.activeDate = date;
25007         if(vd && this.el){
25008             var t = date.getTime();
25009             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25010                 this.cells.removeClass("x-date-selected");
25011                 this.cells.each(function(c){
25012                    if(c.dom.firstChild.dateValue == t){
25013                        c.addClass("x-date-selected");
25014                        setTimeout(function(){
25015                             try{c.dom.firstChild.focus();}catch(e){}
25016                        }, 50);
25017                        return false;
25018                    }
25019                 });
25020                 return;
25021             }
25022         }
25023         var days = date.getDaysInMonth();
25024         var firstOfMonth = date.getFirstDateOfMonth();
25025         var startingPos = firstOfMonth.getDay()-this.startDay;
25026
25027         if(startingPos <= this.startDay){
25028             startingPos += 7;
25029         }
25030
25031         var pm = date.add("mo", -1);
25032         var prevStart = pm.getDaysInMonth()-startingPos;
25033
25034         var cells = this.cells.elements;
25035         var textEls = this.textNodes;
25036         days += startingPos;
25037
25038         // convert everything to numbers so it's fast
25039         var day = 86400000;
25040         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25041         var today = new Date().clearTime().getTime();
25042         var sel = date.clearTime().getTime();
25043         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25044         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25045         var ddMatch = this.disabledDatesRE;
25046         var ddText = this.disabledDatesText;
25047         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25048         var ddaysText = this.disabledDaysText;
25049         var format = this.format;
25050
25051         var setCellClass = function(cal, cell){
25052             cell.title = "";
25053             var t = d.getTime();
25054             cell.firstChild.dateValue = t;
25055             if(t == today){
25056                 cell.className += " x-date-today";
25057                 cell.title = cal.todayText;
25058             }
25059             if(t == sel){
25060                 cell.className += " x-date-selected";
25061                 setTimeout(function(){
25062                     try{cell.firstChild.focus();}catch(e){}
25063                 }, 50);
25064             }
25065             // disabling
25066             if(t < min) {
25067                 cell.className = " x-date-disabled";
25068                 cell.title = cal.minText;
25069                 return;
25070             }
25071             if(t > max) {
25072                 cell.className = " x-date-disabled";
25073                 cell.title = cal.maxText;
25074                 return;
25075             }
25076             if(ddays){
25077                 if(ddays.indexOf(d.getDay()) != -1){
25078                     cell.title = ddaysText;
25079                     cell.className = " x-date-disabled";
25080                 }
25081             }
25082             if(ddMatch && format){
25083                 var fvalue = d.dateFormat(format);
25084                 if(ddMatch.test(fvalue)){
25085                     cell.title = ddText.replace("%0", fvalue);
25086                     cell.className = " x-date-disabled";
25087                 }
25088             }
25089         };
25090
25091         var i = 0;
25092         for(; i < startingPos; i++) {
25093             textEls[i].innerHTML = (++prevStart);
25094             d.setDate(d.getDate()+1);
25095             cells[i].className = "x-date-prevday";
25096             setCellClass(this, cells[i]);
25097         }
25098         for(; i < days; i++){
25099             intDay = i - startingPos + 1;
25100             textEls[i].innerHTML = (intDay);
25101             d.setDate(d.getDate()+1);
25102             cells[i].className = "x-date-active";
25103             setCellClass(this, cells[i]);
25104         }
25105         var extraDays = 0;
25106         for(; i < 42; i++) {
25107              textEls[i].innerHTML = (++extraDays);
25108              d.setDate(d.getDate()+1);
25109              cells[i].className = "x-date-nextday";
25110              setCellClass(this, cells[i]);
25111         }
25112
25113         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25114
25115         if(!this.internalRender){
25116             var main = this.el.dom.firstChild;
25117             var w = main.offsetWidth;
25118             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25119             Roo.fly(main).setWidth(w);
25120             this.internalRender = true;
25121             // opera does not respect the auto grow header center column
25122             // then, after it gets a width opera refuses to recalculate
25123             // without a second pass
25124             if(Roo.isOpera && !this.secondPass){
25125                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25126                 this.secondPass = true;
25127                 this.update.defer(10, this, [date]);
25128             }
25129         }
25130     }
25131 });/*
25132  * Based on:
25133  * Ext JS Library 1.1.1
25134  * Copyright(c) 2006-2007, Ext JS, LLC.
25135  *
25136  * Originally Released Under LGPL - original licence link has changed is not relivant.
25137  *
25138  * Fork - LGPL
25139  * <script type="text/javascript">
25140  */
25141 /**
25142  * @class Roo.TabPanel
25143  * @extends Roo.util.Observable
25144  * A lightweight tab container.
25145  * <br><br>
25146  * Usage:
25147  * <pre><code>
25148 // basic tabs 1, built from existing content
25149 var tabs = new Roo.TabPanel("tabs1");
25150 tabs.addTab("script", "View Script");
25151 tabs.addTab("markup", "View Markup");
25152 tabs.activate("script");
25153
25154 // more advanced tabs, built from javascript
25155 var jtabs = new Roo.TabPanel("jtabs");
25156 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25157
25158 // set up the UpdateManager
25159 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25160 var updater = tab2.getUpdateManager();
25161 updater.setDefaultUrl("ajax1.htm");
25162 tab2.on('activate', updater.refresh, updater, true);
25163
25164 // Use setUrl for Ajax loading
25165 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25166 tab3.setUrl("ajax2.htm", null, true);
25167
25168 // Disabled tab
25169 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25170 tab4.disable();
25171
25172 jtabs.activate("jtabs-1");
25173  * </code></pre>
25174  * @constructor
25175  * Create a new TabPanel.
25176  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25177  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25178  */
25179 Roo.TabPanel = function(container, config){
25180     /**
25181     * The container element for this TabPanel.
25182     * @type Roo.Element
25183     */
25184     this.el = Roo.get(container, true);
25185     if(config){
25186         if(typeof config == "boolean"){
25187             this.tabPosition = config ? "bottom" : "top";
25188         }else{
25189             Roo.apply(this, config);
25190         }
25191     }
25192     if(this.tabPosition == "bottom"){
25193         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25194         this.el.addClass("x-tabs-bottom");
25195     }
25196     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25197     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25198     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25199     if(Roo.isIE){
25200         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25201     }
25202     if(this.tabPosition != "bottom"){
25203     /** The body element that contains {@link Roo.TabPanelItem} bodies.
25204      * @type Roo.Element
25205      */
25206       this.bodyEl = Roo.get(this.createBody(this.el.dom));
25207       this.el.addClass("x-tabs-top");
25208     }
25209     this.items = [];
25210
25211     this.bodyEl.setStyle("position", "relative");
25212
25213     this.active = null;
25214     this.activateDelegate = this.activate.createDelegate(this);
25215
25216     this.addEvents({
25217         /**
25218          * @event tabchange
25219          * Fires when the active tab changes
25220          * @param {Roo.TabPanel} this
25221          * @param {Roo.TabPanelItem} activePanel The new active tab
25222          */
25223         "tabchange": true,
25224         /**
25225          * @event beforetabchange
25226          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25227          * @param {Roo.TabPanel} this
25228          * @param {Object} e Set cancel to true on this object to cancel the tab change
25229          * @param {Roo.TabPanelItem} tab The tab being changed to
25230          */
25231         "beforetabchange" : true
25232     });
25233
25234     Roo.EventManager.onWindowResize(this.onResize, this);
25235     this.cpad = this.el.getPadding("lr");
25236     this.hiddenCount = 0;
25237
25238     Roo.TabPanel.superclass.constructor.call(this);
25239 };
25240
25241 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25242         /*
25243          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25244          */
25245     tabPosition : "top",
25246         /*
25247          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25248          */
25249     currentTabWidth : 0,
25250         /*
25251          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25252          */
25253     minTabWidth : 40,
25254         /*
25255          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25256          */
25257     maxTabWidth : 250,
25258         /*
25259          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25260          */
25261     preferredTabWidth : 175,
25262         /*
25263          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25264          */
25265     resizeTabs : false,
25266         /*
25267          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25268          */
25269     monitorResize : true,
25270
25271     /**
25272      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25273      * @param {String} id The id of the div to use <b>or create</b>
25274      * @param {String} text The text for the tab
25275      * @param {String} content (optional) Content to put in the TabPanelItem body
25276      * @param {Boolean} closable (optional) True to create a close icon on the tab
25277      * @return {Roo.TabPanelItem} The created TabPanelItem
25278      */
25279     addTab : function(id, text, content, closable){
25280         var item = new Roo.TabPanelItem(this, id, text, closable);
25281         this.addTabItem(item);
25282         if(content){
25283             item.setContent(content);
25284         }
25285         return item;
25286     },
25287
25288     /**
25289      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25290      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25291      * @return {Roo.TabPanelItem}
25292      */
25293     getTab : function(id){
25294         return this.items[id];
25295     },
25296
25297     /**
25298      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25299      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25300      */
25301     hideTab : function(id){
25302         var t = this.items[id];
25303         if(!t.isHidden()){
25304            t.setHidden(true);
25305            this.hiddenCount++;
25306            this.autoSizeTabs();
25307         }
25308     },
25309
25310     /**
25311      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25312      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25313      */
25314     unhideTab : function(id){
25315         var t = this.items[id];
25316         if(t.isHidden()){
25317            t.setHidden(false);
25318            this.hiddenCount--;
25319            this.autoSizeTabs();
25320         }
25321     },
25322
25323     /**
25324      * Adds an existing {@link Roo.TabPanelItem}.
25325      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25326      */
25327     addTabItem : function(item){
25328         this.items[item.id] = item;
25329         this.items.push(item);
25330         if(this.resizeTabs){
25331            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25332            this.autoSizeTabs();
25333         }else{
25334             item.autoSize();
25335         }
25336     },
25337
25338     /**
25339      * Removes a {@link Roo.TabPanelItem}.
25340      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25341      */
25342     removeTab : function(id){
25343         var items = this.items;
25344         var tab = items[id];
25345         if(!tab) { return; }
25346         var index = items.indexOf(tab);
25347         if(this.active == tab && items.length > 1){
25348             var newTab = this.getNextAvailable(index);
25349             if(newTab) {
25350                 newTab.activate();
25351             }
25352         }
25353         this.stripEl.dom.removeChild(tab.pnode.dom);
25354         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25355             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25356         }
25357         items.splice(index, 1);
25358         delete this.items[tab.id];
25359         tab.fireEvent("close", tab);
25360         tab.purgeListeners();
25361         this.autoSizeTabs();
25362     },
25363
25364     getNextAvailable : function(start){
25365         var items = this.items;
25366         var index = start;
25367         // look for a next tab that will slide over to
25368         // replace the one being removed
25369         while(index < items.length){
25370             var item = items[++index];
25371             if(item && !item.isHidden()){
25372                 return item;
25373             }
25374         }
25375         // if one isn't found select the previous tab (on the left)
25376         index = start;
25377         while(index >= 0){
25378             var item = items[--index];
25379             if(item && !item.isHidden()){
25380                 return item;
25381             }
25382         }
25383         return null;
25384     },
25385
25386     /**
25387      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25388      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25389      */
25390     disableTab : function(id){
25391         var tab = this.items[id];
25392         if(tab && this.active != tab){
25393             tab.disable();
25394         }
25395     },
25396
25397     /**
25398      * Enables a {@link Roo.TabPanelItem} that is disabled.
25399      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25400      */
25401     enableTab : function(id){
25402         var tab = this.items[id];
25403         tab.enable();
25404     },
25405
25406     /**
25407      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25408      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25409      * @return {Roo.TabPanelItem} The TabPanelItem.
25410      */
25411     activate : function(id){
25412         var tab = this.items[id];
25413         if(!tab){
25414             return null;
25415         }
25416         if(tab == this.active || tab.disabled){
25417             return tab;
25418         }
25419         var e = {};
25420         this.fireEvent("beforetabchange", this, e, tab);
25421         if(e.cancel !== true && !tab.disabled){
25422             if(this.active){
25423                 this.active.hide();
25424             }
25425             this.active = this.items[id];
25426             this.active.show();
25427             this.fireEvent("tabchange", this, this.active);
25428         }
25429         return tab;
25430     },
25431
25432     /**
25433      * Gets the active {@link Roo.TabPanelItem}.
25434      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25435      */
25436     getActiveTab : function(){
25437         return this.active;
25438     },
25439
25440     /**
25441      * Updates the tab body element to fit the height of the container element
25442      * for overflow scrolling
25443      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25444      */
25445     syncHeight : function(targetHeight){
25446         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25447         var bm = this.bodyEl.getMargins();
25448         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25449         this.bodyEl.setHeight(newHeight);
25450         return newHeight;
25451     },
25452
25453     onResize : function(){
25454         if(this.monitorResize){
25455             this.autoSizeTabs();
25456         }
25457     },
25458
25459     /**
25460      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25461      */
25462     beginUpdate : function(){
25463         this.updating = true;
25464     },
25465
25466     /**
25467      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25468      */
25469     endUpdate : function(){
25470         this.updating = false;
25471         this.autoSizeTabs();
25472     },
25473
25474     /**
25475      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25476      */
25477     autoSizeTabs : function(){
25478         var count = this.items.length;
25479         var vcount = count - this.hiddenCount;
25480         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25481         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25482         var availWidth = Math.floor(w / vcount);
25483         var b = this.stripBody;
25484         if(b.getWidth() > w){
25485             var tabs = this.items;
25486             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25487             if(availWidth < this.minTabWidth){
25488                 /*if(!this.sleft){    // incomplete scrolling code
25489                     this.createScrollButtons();
25490                 }
25491                 this.showScroll();
25492                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25493             }
25494         }else{
25495             if(this.currentTabWidth < this.preferredTabWidth){
25496                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25497             }
25498         }
25499     },
25500
25501     /**
25502      * Returns the number of tabs in this TabPanel.
25503      * @return {Number}
25504      */
25505      getCount : function(){
25506          return this.items.length;
25507      },
25508
25509     /**
25510      * Resizes all the tabs to the passed width
25511      * @param {Number} The new width
25512      */
25513     setTabWidth : function(width){
25514         this.currentTabWidth = width;
25515         for(var i = 0, len = this.items.length; i < len; i++) {
25516                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25517         }
25518     },
25519
25520     /**
25521      * Destroys this TabPanel
25522      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25523      */
25524     destroy : function(removeEl){
25525         Roo.EventManager.removeResizeListener(this.onResize, this);
25526         for(var i = 0, len = this.items.length; i < len; i++){
25527             this.items[i].purgeListeners();
25528         }
25529         if(removeEl === true){
25530             this.el.update("");
25531             this.el.remove();
25532         }
25533     }
25534 });
25535
25536 /**
25537  * @class Roo.TabPanelItem
25538  * @extends Roo.util.Observable
25539  * Represents an individual item (tab plus body) in a TabPanel.
25540  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25541  * @param {String} id The id of this TabPanelItem
25542  * @param {String} text The text for the tab of this TabPanelItem
25543  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25544  */
25545 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25546     /**
25547      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25548      * @type Roo.TabPanel
25549      */
25550     this.tabPanel = tabPanel;
25551     /**
25552      * The id for this TabPanelItem
25553      * @type String
25554      */
25555     this.id = id;
25556     /** @private */
25557     this.disabled = false;
25558     /** @private */
25559     this.text = text;
25560     /** @private */
25561     this.loaded = false;
25562     this.closable = closable;
25563
25564     /**
25565      * The body element for this TabPanelItem.
25566      * @type Roo.Element
25567      */
25568     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25569     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25570     this.bodyEl.setStyle("display", "block");
25571     this.bodyEl.setStyle("zoom", "1");
25572     this.hideAction();
25573
25574     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25575     /** @private */
25576     this.el = Roo.get(els.el, true);
25577     this.inner = Roo.get(els.inner, true);
25578     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25579     this.pnode = Roo.get(els.el.parentNode, true);
25580     this.el.on("mousedown", this.onTabMouseDown, this);
25581     this.el.on("click", this.onTabClick, this);
25582     /** @private */
25583     if(closable){
25584         var c = Roo.get(els.close, true);
25585         c.dom.title = this.closeText;
25586         c.addClassOnOver("close-over");
25587         c.on("click", this.closeClick, this);
25588      }
25589
25590     this.addEvents({
25591          /**
25592          * @event activate
25593          * Fires when this tab becomes the active tab.
25594          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25595          * @param {Roo.TabPanelItem} this
25596          */
25597         "activate": true,
25598         /**
25599          * @event beforeclose
25600          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25601          * @param {Roo.TabPanelItem} this
25602          * @param {Object} e Set cancel to true on this object to cancel the close.
25603          */
25604         "beforeclose": true,
25605         /**
25606          * @event close
25607          * Fires when this tab is closed.
25608          * @param {Roo.TabPanelItem} this
25609          */
25610          "close": true,
25611         /**
25612          * @event deactivate
25613          * Fires when this tab is no longer the active tab.
25614          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25615          * @param {Roo.TabPanelItem} this
25616          */
25617          "deactivate" : true
25618     });
25619     this.hidden = false;
25620
25621     Roo.TabPanelItem.superclass.constructor.call(this);
25622 };
25623
25624 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25625     purgeListeners : function(){
25626        Roo.util.Observable.prototype.purgeListeners.call(this);
25627        this.el.removeAllListeners();
25628     },
25629     /**
25630      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25631      */
25632     show : function(){
25633         this.pnode.addClass("on");
25634         this.showAction();
25635         if(Roo.isOpera){
25636             this.tabPanel.stripWrap.repaint();
25637         }
25638         this.fireEvent("activate", this.tabPanel, this);
25639     },
25640
25641     /**
25642      * Returns true if this tab is the active tab.
25643      * @return {Boolean}
25644      */
25645     isActive : function(){
25646         return this.tabPanel.getActiveTab() == this;
25647     },
25648
25649     /**
25650      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25651      */
25652     hide : function(){
25653         this.pnode.removeClass("on");
25654         this.hideAction();
25655         this.fireEvent("deactivate", this.tabPanel, this);
25656     },
25657
25658     hideAction : function(){
25659         this.bodyEl.hide();
25660         this.bodyEl.setStyle("position", "absolute");
25661         this.bodyEl.setLeft("-20000px");
25662         this.bodyEl.setTop("-20000px");
25663     },
25664
25665     showAction : function(){
25666         this.bodyEl.setStyle("position", "relative");
25667         this.bodyEl.setTop("");
25668         this.bodyEl.setLeft("");
25669         this.bodyEl.show();
25670     },
25671
25672     /**
25673      * Set the tooltip for the tab.
25674      * @param {String} tooltip The tab's tooltip
25675      */
25676     setTooltip : function(text){
25677         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25678             this.textEl.dom.qtip = text;
25679             this.textEl.dom.removeAttribute('title');
25680         }else{
25681             this.textEl.dom.title = text;
25682         }
25683     },
25684
25685     onTabClick : function(e){
25686         e.preventDefault();
25687         this.tabPanel.activate(this.id);
25688     },
25689
25690     onTabMouseDown : function(e){
25691         e.preventDefault();
25692         this.tabPanel.activate(this.id);
25693     },
25694
25695     getWidth : function(){
25696         return this.inner.getWidth();
25697     },
25698
25699     setWidth : function(width){
25700         var iwidth = width - this.pnode.getPadding("lr");
25701         this.inner.setWidth(iwidth);
25702         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25703         this.pnode.setWidth(width);
25704     },
25705
25706     /**
25707      * Show or hide the tab
25708      * @param {Boolean} hidden True to hide or false to show.
25709      */
25710     setHidden : function(hidden){
25711         this.hidden = hidden;
25712         this.pnode.setStyle("display", hidden ? "none" : "");
25713     },
25714
25715     /**
25716      * Returns true if this tab is "hidden"
25717      * @return {Boolean}
25718      */
25719     isHidden : function(){
25720         return this.hidden;
25721     },
25722
25723     /**
25724      * Returns the text for this tab
25725      * @return {String}
25726      */
25727     getText : function(){
25728         return this.text;
25729     },
25730
25731     autoSize : function(){
25732         //this.el.beginMeasure();
25733         this.textEl.setWidth(1);
25734         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25735         //this.el.endMeasure();
25736     },
25737
25738     /**
25739      * Sets the text for the tab (Note: this also sets the tooltip text)
25740      * @param {String} text The tab's text and tooltip
25741      */
25742     setText : function(text){
25743         this.text = text;
25744         this.textEl.update(text);
25745         this.setTooltip(text);
25746         if(!this.tabPanel.resizeTabs){
25747             this.autoSize();
25748         }
25749     },
25750     /**
25751      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25752      */
25753     activate : function(){
25754         this.tabPanel.activate(this.id);
25755     },
25756
25757     /**
25758      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25759      */
25760     disable : function(){
25761         if(this.tabPanel.active != this){
25762             this.disabled = true;
25763             this.pnode.addClass("disabled");
25764         }
25765     },
25766
25767     /**
25768      * Enables this TabPanelItem if it was previously disabled.
25769      */
25770     enable : function(){
25771         this.disabled = false;
25772         this.pnode.removeClass("disabled");
25773     },
25774
25775     /**
25776      * Sets the content for this TabPanelItem.
25777      * @param {String} content The content
25778      * @param {Boolean} loadScripts true to look for and load scripts
25779      */
25780     setContent : function(content, loadScripts){
25781         this.bodyEl.update(content, loadScripts);
25782     },
25783
25784     /**
25785      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25786      * @return {Roo.UpdateManager} The UpdateManager
25787      */
25788     getUpdateManager : function(){
25789         return this.bodyEl.getUpdateManager();
25790     },
25791
25792     /**
25793      * Set a URL to be used to load the content for this TabPanelItem.
25794      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25795      * @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)
25796      * @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)
25797      * @return {Roo.UpdateManager} The UpdateManager
25798      */
25799     setUrl : function(url, params, loadOnce){
25800         if(this.refreshDelegate){
25801             this.un('activate', this.refreshDelegate);
25802         }
25803         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25804         this.on("activate", this.refreshDelegate);
25805         return this.bodyEl.getUpdateManager();
25806     },
25807
25808     /** @private */
25809     _handleRefresh : function(url, params, loadOnce){
25810         if(!loadOnce || !this.loaded){
25811             var updater = this.bodyEl.getUpdateManager();
25812             updater.update(url, params, this._setLoaded.createDelegate(this));
25813         }
25814     },
25815
25816     /**
25817      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25818      *   Will fail silently if the setUrl method has not been called.
25819      *   This does not activate the panel, just updates its content.
25820      */
25821     refresh : function(){
25822         if(this.refreshDelegate){
25823            this.loaded = false;
25824            this.refreshDelegate();
25825         }
25826     },
25827
25828     /** @private */
25829     _setLoaded : function(){
25830         this.loaded = true;
25831     },
25832
25833     /** @private */
25834     closeClick : function(e){
25835         var o = {};
25836         e.stopEvent();
25837         this.fireEvent("beforeclose", this, o);
25838         if(o.cancel !== true){
25839             this.tabPanel.removeTab(this.id);
25840         }
25841     },
25842     /**
25843      * The text displayed in the tooltip for the close icon.
25844      * @type String
25845      */
25846     closeText : "Close this tab"
25847 });
25848
25849 /** @private */
25850 Roo.TabPanel.prototype.createStrip = function(container){
25851     var strip = document.createElement("div");
25852     strip.className = "x-tabs-wrap";
25853     container.appendChild(strip);
25854     return strip;
25855 };
25856 /** @private */
25857 Roo.TabPanel.prototype.createStripList = function(strip){
25858     // div wrapper for retard IE
25859     strip.innerHTML = '<div class="x-tabs-strip-wrap"><table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr></tr></tbody></table></div>';
25860     return strip.firstChild.firstChild.firstChild.firstChild;
25861 };
25862 /** @private */
25863 Roo.TabPanel.prototype.createBody = function(container){
25864     var body = document.createElement("div");
25865     Roo.id(body, "tab-body");
25866     Roo.fly(body).addClass("x-tabs-body");
25867     container.appendChild(body);
25868     return body;
25869 };
25870 /** @private */
25871 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25872     var body = Roo.getDom(id);
25873     if(!body){
25874         body = document.createElement("div");
25875         body.id = id;
25876     }
25877     Roo.fly(body).addClass("x-tabs-item-body");
25878     bodyEl.insertBefore(body, bodyEl.firstChild);
25879     return body;
25880 };
25881 /** @private */
25882 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25883     var td = document.createElement("td");
25884     stripEl.appendChild(td);
25885     if(closable){
25886         td.className = "x-tabs-closable";
25887         if(!this.closeTpl){
25888             this.closeTpl = new Roo.Template(
25889                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25890                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25891                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25892             );
25893         }
25894         var el = this.closeTpl.overwrite(td, {"text": text});
25895         var close = el.getElementsByTagName("div")[0];
25896         var inner = el.getElementsByTagName("em")[0];
25897         return {"el": el, "close": close, "inner": inner};
25898     } else {
25899         if(!this.tabTpl){
25900             this.tabTpl = new Roo.Template(
25901                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25902                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25903             );
25904         }
25905         var el = this.tabTpl.overwrite(td, {"text": text});
25906         var inner = el.getElementsByTagName("em")[0];
25907         return {"el": el, "inner": inner};
25908     }
25909 };/*
25910  * Based on:
25911  * Ext JS Library 1.1.1
25912  * Copyright(c) 2006-2007, Ext JS, LLC.
25913  *
25914  * Originally Released Under LGPL - original licence link has changed is not relivant.
25915  *
25916  * Fork - LGPL
25917  * <script type="text/javascript">
25918  */
25919
25920 /**
25921  * @class Roo.Button
25922  * @extends Roo.util.Observable
25923  * Simple Button class
25924  * @cfg {String} text The button text
25925  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25926  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25927  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25928  * @cfg {Object} scope The scope of the handler
25929  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25930  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25931  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25932  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25933  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25934  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25935    applies if enableToggle = true)
25936  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25937  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25938   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25939  * @constructor
25940  * Create a new button
25941  * @param {Object} config The config object
25942  */
25943 Roo.Button = function(renderTo, config)
25944 {
25945     if (!config) {
25946         config = renderTo;
25947         renderTo = config.renderTo || false;
25948     }
25949     
25950     Roo.apply(this, config);
25951     this.addEvents({
25952         /**
25953              * @event click
25954              * Fires when this button is clicked
25955              * @param {Button} this
25956              * @param {EventObject} e The click event
25957              */
25958             "click" : true,
25959         /**
25960              * @event toggle
25961              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
25962              * @param {Button} this
25963              * @param {Boolean} pressed
25964              */
25965             "toggle" : true,
25966         /**
25967              * @event mouseover
25968              * Fires when the mouse hovers over the button
25969              * @param {Button} this
25970              * @param {Event} e The event object
25971              */
25972         'mouseover' : true,
25973         /**
25974              * @event mouseout
25975              * Fires when the mouse exits the button
25976              * @param {Button} this
25977              * @param {Event} e The event object
25978              */
25979         'mouseout': true,
25980          /**
25981              * @event render
25982              * Fires when the button is rendered
25983              * @param {Button} this
25984              */
25985         'render': true
25986     });
25987     if(this.menu){
25988         this.menu = Roo.menu.MenuMgr.get(this.menu);
25989     }
25990     // register listeners first!!  - so render can be captured..
25991     Roo.util.Observable.call(this);
25992     if(renderTo){
25993         this.render(renderTo);
25994     }
25995     
25996   
25997 };
25998
25999 Roo.extend(Roo.Button, Roo.util.Observable, {
26000     /**
26001      * 
26002      */
26003     
26004     /**
26005      * Read-only. True if this button is hidden
26006      * @type Boolean
26007      */
26008     hidden : false,
26009     /**
26010      * Read-only. True if this button is disabled
26011      * @type Boolean
26012      */
26013     disabled : false,
26014     /**
26015      * Read-only. True if this button is pressed (only if enableToggle = true)
26016      * @type Boolean
26017      */
26018     pressed : false,
26019
26020     /**
26021      * @cfg {Number} tabIndex 
26022      * The DOM tabIndex for this button (defaults to undefined)
26023      */
26024     tabIndex : undefined,
26025
26026     /**
26027      * @cfg {Boolean} enableToggle
26028      * True to enable pressed/not pressed toggling (defaults to false)
26029      */
26030     enableToggle: false,
26031     /**
26032      * @cfg {Mixed} menu
26033      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26034      */
26035     menu : undefined,
26036     /**
26037      * @cfg {String} menuAlign
26038      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26039      */
26040     menuAlign : "tl-bl?",
26041
26042     /**
26043      * @cfg {String} iconCls
26044      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26045      */
26046     iconCls : undefined,
26047     /**
26048      * @cfg {String} type
26049      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26050      */
26051     type : 'button',
26052
26053     // private
26054     menuClassTarget: 'tr',
26055
26056     /**
26057      * @cfg {String} clickEvent
26058      * The type of event to map to the button's event handler (defaults to 'click')
26059      */
26060     clickEvent : 'click',
26061
26062     /**
26063      * @cfg {Boolean} handleMouseEvents
26064      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26065      */
26066     handleMouseEvents : true,
26067
26068     /**
26069      * @cfg {String} tooltipType
26070      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26071      */
26072     tooltipType : 'qtip',
26073
26074     /**
26075      * @cfg {String} cls
26076      * A CSS class to apply to the button's main element.
26077      */
26078     
26079     /**
26080      * @cfg {Roo.Template} template (Optional)
26081      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26082      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26083      * require code modifications if required elements (e.g. a button) aren't present.
26084      */
26085
26086     // private
26087     render : function(renderTo){
26088         var btn;
26089         if(this.hideParent){
26090             this.parentEl = Roo.get(renderTo);
26091         }
26092         if(!this.dhconfig){
26093             if(!this.template){
26094                 if(!Roo.Button.buttonTemplate){
26095                     // hideous table template
26096                     Roo.Button.buttonTemplate = new Roo.Template(
26097                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26098                         '<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>',
26099                         "</tr></tbody></table>");
26100                 }
26101                 this.template = Roo.Button.buttonTemplate;
26102             }
26103             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26104             var btnEl = btn.child("button:first");
26105             btnEl.on('focus', this.onFocus, this);
26106             btnEl.on('blur', this.onBlur, this);
26107             if(this.cls){
26108                 btn.addClass(this.cls);
26109             }
26110             if(this.icon){
26111                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26112             }
26113             if(this.iconCls){
26114                 btnEl.addClass(this.iconCls);
26115                 if(!this.cls){
26116                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26117                 }
26118             }
26119             if(this.tabIndex !== undefined){
26120                 btnEl.dom.tabIndex = this.tabIndex;
26121             }
26122             if(this.tooltip){
26123                 if(typeof this.tooltip == 'object'){
26124                     Roo.QuickTips.tips(Roo.apply({
26125                           target: btnEl.id
26126                     }, this.tooltip));
26127                 } else {
26128                     btnEl.dom[this.tooltipType] = this.tooltip;
26129                 }
26130             }
26131         }else{
26132             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26133         }
26134         this.el = btn;
26135         if(this.id){
26136             this.el.dom.id = this.el.id = this.id;
26137         }
26138         if(this.menu){
26139             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26140             this.menu.on("show", this.onMenuShow, this);
26141             this.menu.on("hide", this.onMenuHide, this);
26142         }
26143         btn.addClass("x-btn");
26144         if(Roo.isIE && !Roo.isIE7){
26145             this.autoWidth.defer(1, this);
26146         }else{
26147             this.autoWidth();
26148         }
26149         if(this.handleMouseEvents){
26150             btn.on("mouseover", this.onMouseOver, this);
26151             btn.on("mouseout", this.onMouseOut, this);
26152             btn.on("mousedown", this.onMouseDown, this);
26153         }
26154         btn.on(this.clickEvent, this.onClick, this);
26155         //btn.on("mouseup", this.onMouseUp, this);
26156         if(this.hidden){
26157             this.hide();
26158         }
26159         if(this.disabled){
26160             this.disable();
26161         }
26162         Roo.ButtonToggleMgr.register(this);
26163         if(this.pressed){
26164             this.el.addClass("x-btn-pressed");
26165         }
26166         if(this.repeat){
26167             var repeater = new Roo.util.ClickRepeater(btn,
26168                 typeof this.repeat == "object" ? this.repeat : {}
26169             );
26170             repeater.on("click", this.onClick,  this);
26171         }
26172         
26173         this.fireEvent('render', this);
26174         
26175     },
26176     /**
26177      * Returns the button's underlying element
26178      * @return {Roo.Element} The element
26179      */
26180     getEl : function(){
26181         return this.el;  
26182     },
26183     
26184     /**
26185      * Destroys this Button and removes any listeners.
26186      */
26187     destroy : function(){
26188         Roo.ButtonToggleMgr.unregister(this);
26189         this.el.removeAllListeners();
26190         this.purgeListeners();
26191         this.el.remove();
26192     },
26193
26194     // private
26195     autoWidth : function(){
26196         if(this.el){
26197             this.el.setWidth("auto");
26198             if(Roo.isIE7 && Roo.isStrict){
26199                 var ib = this.el.child('button');
26200                 if(ib && ib.getWidth() > 20){
26201                     ib.clip();
26202                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26203                 }
26204             }
26205             if(this.minWidth){
26206                 if(this.hidden){
26207                     this.el.beginMeasure();
26208                 }
26209                 if(this.el.getWidth() < this.minWidth){
26210                     this.el.setWidth(this.minWidth);
26211                 }
26212                 if(this.hidden){
26213                     this.el.endMeasure();
26214                 }
26215             }
26216         }
26217     },
26218
26219     /**
26220      * Assigns this button's click handler
26221      * @param {Function} handler The function to call when the button is clicked
26222      * @param {Object} scope (optional) Scope for the function passed in
26223      */
26224     setHandler : function(handler, scope){
26225         this.handler = handler;
26226         this.scope = scope;  
26227     },
26228     
26229     /**
26230      * Sets this button's text
26231      * @param {String} text The button text
26232      */
26233     setText : function(text){
26234         this.text = text;
26235         if(this.el){
26236             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26237         }
26238         this.autoWidth();
26239     },
26240     
26241     /**
26242      * Gets the text for this button
26243      * @return {String} The button text
26244      */
26245     getText : function(){
26246         return this.text;  
26247     },
26248     
26249     /**
26250      * Show this button
26251      */
26252     show: function(){
26253         this.hidden = false;
26254         if(this.el){
26255             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26256         }
26257     },
26258     
26259     /**
26260      * Hide this button
26261      */
26262     hide: function(){
26263         this.hidden = true;
26264         if(this.el){
26265             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26266         }
26267     },
26268     
26269     /**
26270      * Convenience function for boolean show/hide
26271      * @param {Boolean} visible True to show, false to hide
26272      */
26273     setVisible: function(visible){
26274         if(visible) {
26275             this.show();
26276         }else{
26277             this.hide();
26278         }
26279     },
26280     
26281     /**
26282      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26283      * @param {Boolean} state (optional) Force a particular state
26284      */
26285     toggle : function(state){
26286         state = state === undefined ? !this.pressed : state;
26287         if(state != this.pressed){
26288             if(state){
26289                 this.el.addClass("x-btn-pressed");
26290                 this.pressed = true;
26291                 this.fireEvent("toggle", this, true);
26292             }else{
26293                 this.el.removeClass("x-btn-pressed");
26294                 this.pressed = false;
26295                 this.fireEvent("toggle", this, false);
26296             }
26297             if(this.toggleHandler){
26298                 this.toggleHandler.call(this.scope || this, this, state);
26299             }
26300         }
26301     },
26302     
26303     /**
26304      * Focus the button
26305      */
26306     focus : function(){
26307         this.el.child('button:first').focus();
26308     },
26309     
26310     /**
26311      * Disable this button
26312      */
26313     disable : function(){
26314         if(this.el){
26315             this.el.addClass("x-btn-disabled");
26316         }
26317         this.disabled = true;
26318     },
26319     
26320     /**
26321      * Enable this button
26322      */
26323     enable : function(){
26324         if(this.el){
26325             this.el.removeClass("x-btn-disabled");
26326         }
26327         this.disabled = false;
26328     },
26329
26330     /**
26331      * Convenience function for boolean enable/disable
26332      * @param {Boolean} enabled True to enable, false to disable
26333      */
26334     setDisabled : function(v){
26335         this[v !== true ? "enable" : "disable"]();
26336     },
26337
26338     // private
26339     onClick : function(e){
26340         if(e){
26341             e.preventDefault();
26342         }
26343         if(e.button != 0){
26344             return;
26345         }
26346         if(!this.disabled){
26347             if(this.enableToggle){
26348                 this.toggle();
26349             }
26350             if(this.menu && !this.menu.isVisible()){
26351                 this.menu.show(this.el, this.menuAlign);
26352             }
26353             this.fireEvent("click", this, e);
26354             if(this.handler){
26355                 this.el.removeClass("x-btn-over");
26356                 this.handler.call(this.scope || this, this, e);
26357             }
26358         }
26359     },
26360     // private
26361     onMouseOver : function(e){
26362         if(!this.disabled){
26363             this.el.addClass("x-btn-over");
26364             this.fireEvent('mouseover', this, e);
26365         }
26366     },
26367     // private
26368     onMouseOut : function(e){
26369         if(!e.within(this.el,  true)){
26370             this.el.removeClass("x-btn-over");
26371             this.fireEvent('mouseout', this, e);
26372         }
26373     },
26374     // private
26375     onFocus : function(e){
26376         if(!this.disabled){
26377             this.el.addClass("x-btn-focus");
26378         }
26379     },
26380     // private
26381     onBlur : function(e){
26382         this.el.removeClass("x-btn-focus");
26383     },
26384     // private
26385     onMouseDown : function(e){
26386         if(!this.disabled && e.button == 0){
26387             this.el.addClass("x-btn-click");
26388             Roo.get(document).on('mouseup', this.onMouseUp, this);
26389         }
26390     },
26391     // private
26392     onMouseUp : function(e){
26393         if(e.button == 0){
26394             this.el.removeClass("x-btn-click");
26395             Roo.get(document).un('mouseup', this.onMouseUp, this);
26396         }
26397     },
26398     // private
26399     onMenuShow : function(e){
26400         this.el.addClass("x-btn-menu-active");
26401     },
26402     // private
26403     onMenuHide : function(e){
26404         this.el.removeClass("x-btn-menu-active");
26405     }   
26406 });
26407
26408 // Private utility class used by Button
26409 Roo.ButtonToggleMgr = function(){
26410    var groups = {};
26411    
26412    function toggleGroup(btn, state){
26413        if(state){
26414            var g = groups[btn.toggleGroup];
26415            for(var i = 0, l = g.length; i < l; i++){
26416                if(g[i] != btn){
26417                    g[i].toggle(false);
26418                }
26419            }
26420        }
26421    }
26422    
26423    return {
26424        register : function(btn){
26425            if(!btn.toggleGroup){
26426                return;
26427            }
26428            var g = groups[btn.toggleGroup];
26429            if(!g){
26430                g = groups[btn.toggleGroup] = [];
26431            }
26432            g.push(btn);
26433            btn.on("toggle", toggleGroup);
26434        },
26435        
26436        unregister : function(btn){
26437            if(!btn.toggleGroup){
26438                return;
26439            }
26440            var g = groups[btn.toggleGroup];
26441            if(g){
26442                g.remove(btn);
26443                btn.un("toggle", toggleGroup);
26444            }
26445        }
26446    };
26447 }();/*
26448  * Based on:
26449  * Ext JS Library 1.1.1
26450  * Copyright(c) 2006-2007, Ext JS, LLC.
26451  *
26452  * Originally Released Under LGPL - original licence link has changed is not relivant.
26453  *
26454  * Fork - LGPL
26455  * <script type="text/javascript">
26456  */
26457  
26458 /**
26459  * @class Roo.SplitButton
26460  * @extends Roo.Button
26461  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26462  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26463  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26464  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26465  * @cfg {String} arrowTooltip The title attribute of the arrow
26466  * @constructor
26467  * Create a new menu button
26468  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26469  * @param {Object} config The config object
26470  */
26471 Roo.SplitButton = function(renderTo, config){
26472     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26473     /**
26474      * @event arrowclick
26475      * Fires when this button's arrow is clicked
26476      * @param {SplitButton} this
26477      * @param {EventObject} e The click event
26478      */
26479     this.addEvents({"arrowclick":true});
26480 };
26481
26482 Roo.extend(Roo.SplitButton, Roo.Button, {
26483     render : function(renderTo){
26484         // this is one sweet looking template!
26485         var tpl = new Roo.Template(
26486             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26487             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26488             '<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>',
26489             "</tbody></table></td><td>",
26490             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26491             '<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>',
26492             "</tbody></table></td></tr></table>"
26493         );
26494         var btn = tpl.append(renderTo, [this.text, this.type], true);
26495         var btnEl = btn.child("button");
26496         if(this.cls){
26497             btn.addClass(this.cls);
26498         }
26499         if(this.icon){
26500             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26501         }
26502         if(this.iconCls){
26503             btnEl.addClass(this.iconCls);
26504             if(!this.cls){
26505                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26506             }
26507         }
26508         this.el = btn;
26509         if(this.handleMouseEvents){
26510             btn.on("mouseover", this.onMouseOver, this);
26511             btn.on("mouseout", this.onMouseOut, this);
26512             btn.on("mousedown", this.onMouseDown, this);
26513             btn.on("mouseup", this.onMouseUp, this);
26514         }
26515         btn.on(this.clickEvent, this.onClick, this);
26516         if(this.tooltip){
26517             if(typeof this.tooltip == 'object'){
26518                 Roo.QuickTips.tips(Roo.apply({
26519                       target: btnEl.id
26520                 }, this.tooltip));
26521             } else {
26522                 btnEl.dom[this.tooltipType] = this.tooltip;
26523             }
26524         }
26525         if(this.arrowTooltip){
26526             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26527         }
26528         if(this.hidden){
26529             this.hide();
26530         }
26531         if(this.disabled){
26532             this.disable();
26533         }
26534         if(this.pressed){
26535             this.el.addClass("x-btn-pressed");
26536         }
26537         if(Roo.isIE && !Roo.isIE7){
26538             this.autoWidth.defer(1, this);
26539         }else{
26540             this.autoWidth();
26541         }
26542         if(this.menu){
26543             this.menu.on("show", this.onMenuShow, this);
26544             this.menu.on("hide", this.onMenuHide, this);
26545         }
26546         this.fireEvent('render', this);
26547     },
26548
26549     // private
26550     autoWidth : function(){
26551         if(this.el){
26552             var tbl = this.el.child("table:first");
26553             var tbl2 = this.el.child("table:last");
26554             this.el.setWidth("auto");
26555             tbl.setWidth("auto");
26556             if(Roo.isIE7 && Roo.isStrict){
26557                 var ib = this.el.child('button:first');
26558                 if(ib && ib.getWidth() > 20){
26559                     ib.clip();
26560                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26561                 }
26562             }
26563             if(this.minWidth){
26564                 if(this.hidden){
26565                     this.el.beginMeasure();
26566                 }
26567                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26568                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26569                 }
26570                 if(this.hidden){
26571                     this.el.endMeasure();
26572                 }
26573             }
26574             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26575         } 
26576     },
26577     /**
26578      * Sets this button's click handler
26579      * @param {Function} handler The function to call when the button is clicked
26580      * @param {Object} scope (optional) Scope for the function passed above
26581      */
26582     setHandler : function(handler, scope){
26583         this.handler = handler;
26584         this.scope = scope;  
26585     },
26586     
26587     /**
26588      * Sets this button's arrow click handler
26589      * @param {Function} handler The function to call when the arrow is clicked
26590      * @param {Object} scope (optional) Scope for the function passed above
26591      */
26592     setArrowHandler : function(handler, scope){
26593         this.arrowHandler = handler;
26594         this.scope = scope;  
26595     },
26596     
26597     /**
26598      * Focus the button
26599      */
26600     focus : function(){
26601         if(this.el){
26602             this.el.child("button:first").focus();
26603         }
26604     },
26605
26606     // private
26607     onClick : function(e){
26608         e.preventDefault();
26609         if(!this.disabled){
26610             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26611                 if(this.menu && !this.menu.isVisible()){
26612                     this.menu.show(this.el, this.menuAlign);
26613                 }
26614                 this.fireEvent("arrowclick", this, e);
26615                 if(this.arrowHandler){
26616                     this.arrowHandler.call(this.scope || this, this, e);
26617                 }
26618             }else{
26619                 this.fireEvent("click", this, e);
26620                 if(this.handler){
26621                     this.handler.call(this.scope || this, this, e);
26622                 }
26623             }
26624         }
26625     },
26626     // private
26627     onMouseDown : function(e){
26628         if(!this.disabled){
26629             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26630         }
26631     },
26632     // private
26633     onMouseUp : function(e){
26634         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26635     }   
26636 });
26637
26638
26639 // backwards compat
26640 Roo.MenuButton = Roo.SplitButton;/*
26641  * Based on:
26642  * Ext JS Library 1.1.1
26643  * Copyright(c) 2006-2007, Ext JS, LLC.
26644  *
26645  * Originally Released Under LGPL - original licence link has changed is not relivant.
26646  *
26647  * Fork - LGPL
26648  * <script type="text/javascript">
26649  */
26650
26651 /**
26652  * @class Roo.Toolbar
26653  * Basic Toolbar class.
26654  * @constructor
26655  * Creates a new Toolbar
26656  * @param {Object} config The config object
26657  */ 
26658 Roo.Toolbar = function(container, buttons, config)
26659 {
26660     /// old consturctor format still supported..
26661     if(container instanceof Array){ // omit the container for later rendering
26662         buttons = container;
26663         config = buttons;
26664         container = null;
26665     }
26666     if (typeof(container) == 'object' && container.xtype) {
26667         config = container;
26668         container = config.container;
26669         buttons = config.buttons; // not really - use items!!
26670     }
26671     var xitems = [];
26672     if (config && config.items) {
26673         xitems = config.items;
26674         delete config.items;
26675     }
26676     Roo.apply(this, config);
26677     this.buttons = buttons;
26678     
26679     if(container){
26680         this.render(container);
26681     }
26682     Roo.each(xitems, function(b) {
26683         this.add(b);
26684     }, this);
26685     
26686 };
26687
26688 Roo.Toolbar.prototype = {
26689     /**
26690      * @cfg {Roo.data.Store} items
26691      * array of button configs or elements to add
26692      */
26693     
26694     /**
26695      * @cfg {String/HTMLElement/Element} container
26696      * The id or element that will contain the toolbar
26697      */
26698     // private
26699     render : function(ct){
26700         this.el = Roo.get(ct);
26701         if(this.cls){
26702             this.el.addClass(this.cls);
26703         }
26704         // using a table allows for vertical alignment
26705         // 100% width is needed by Safari...
26706         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26707         this.tr = this.el.child("tr", true);
26708         var autoId = 0;
26709         this.items = new Roo.util.MixedCollection(false, function(o){
26710             return o.id || ("item" + (++autoId));
26711         });
26712         if(this.buttons){
26713             this.add.apply(this, this.buttons);
26714             delete this.buttons;
26715         }
26716     },
26717
26718     /**
26719      * Adds element(s) to the toolbar -- this function takes a variable number of 
26720      * arguments of mixed type and adds them to the toolbar.
26721      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26722      * <ul>
26723      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26724      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26725      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26726      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26727      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26728      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26729      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26730      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26731      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26732      * </ul>
26733      * @param {Mixed} arg2
26734      * @param {Mixed} etc.
26735      */
26736     add : function(){
26737         var a = arguments, l = a.length;
26738         for(var i = 0; i < l; i++){
26739             this._add(a[i]);
26740         }
26741     },
26742     // private..
26743     _add : function(el) {
26744         
26745         if (el.xtype) {
26746             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26747         }
26748         
26749         if (el.applyTo){ // some kind of form field
26750             return this.addField(el);
26751         } 
26752         if (el.render){ // some kind of Toolbar.Item
26753             return this.addItem(el);
26754         }
26755         if (typeof el == "string"){ // string
26756             if(el == "separator" || el == "-"){
26757                 return this.addSeparator();
26758             }
26759             if (el == " "){
26760                 return this.addSpacer();
26761             }
26762             if(el == "->"){
26763                 return this.addFill();
26764             }
26765             return this.addText(el);
26766             
26767         }
26768         if(el.tagName){ // element
26769             return this.addElement(el);
26770         }
26771         if(typeof el == "object"){ // must be button config?
26772             return this.addButton(el);
26773         }
26774         // and now what?!?!
26775         return false;
26776         
26777     },
26778     
26779     /**
26780      * Add an Xtype element
26781      * @param {Object} xtype Xtype Object
26782      * @return {Object} created Object
26783      */
26784     addxtype : function(e){
26785         return this.add(e);  
26786     },
26787     
26788     /**
26789      * Returns the Element for this toolbar.
26790      * @return {Roo.Element}
26791      */
26792     getEl : function(){
26793         return this.el;  
26794     },
26795     
26796     /**
26797      * Adds a separator
26798      * @return {Roo.Toolbar.Item} The separator item
26799      */
26800     addSeparator : function(){
26801         return this.addItem(new Roo.Toolbar.Separator());
26802     },
26803
26804     /**
26805      * Adds a spacer element
26806      * @return {Roo.Toolbar.Spacer} The spacer item
26807      */
26808     addSpacer : function(){
26809         return this.addItem(new Roo.Toolbar.Spacer());
26810     },
26811
26812     /**
26813      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26814      * @return {Roo.Toolbar.Fill} The fill item
26815      */
26816     addFill : function(){
26817         return this.addItem(new Roo.Toolbar.Fill());
26818     },
26819
26820     /**
26821      * Adds any standard HTML element to the toolbar
26822      * @param {String/HTMLElement/Element} el The element or id of the element to add
26823      * @return {Roo.Toolbar.Item} The element's item
26824      */
26825     addElement : function(el){
26826         return this.addItem(new Roo.Toolbar.Item(el));
26827     },
26828     /**
26829      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26830      * @type Roo.util.MixedCollection  
26831      */
26832     items : false,
26833      
26834     /**
26835      * Adds any Toolbar.Item or subclass
26836      * @param {Roo.Toolbar.Item} item
26837      * @return {Roo.Toolbar.Item} The item
26838      */
26839     addItem : function(item){
26840         var td = this.nextBlock();
26841         item.render(td);
26842         this.items.add(item);
26843         return item;
26844     },
26845     
26846     /**
26847      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26848      * @param {Object/Array} config A button config or array of configs
26849      * @return {Roo.Toolbar.Button/Array}
26850      */
26851     addButton : function(config){
26852         if(config instanceof Array){
26853             var buttons = [];
26854             for(var i = 0, len = config.length; i < len; i++) {
26855                 buttons.push(this.addButton(config[i]));
26856             }
26857             return buttons;
26858         }
26859         var b = config;
26860         if(!(config instanceof Roo.Toolbar.Button)){
26861             b = config.split ?
26862                 new Roo.Toolbar.SplitButton(config) :
26863                 new Roo.Toolbar.Button(config);
26864         }
26865         var td = this.nextBlock();
26866         b.render(td);
26867         this.items.add(b);
26868         return b;
26869     },
26870     
26871     /**
26872      * Adds text to the toolbar
26873      * @param {String} text The text to add
26874      * @return {Roo.Toolbar.Item} The element's item
26875      */
26876     addText : function(text){
26877         return this.addItem(new Roo.Toolbar.TextItem(text));
26878     },
26879     
26880     /**
26881      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26882      * @param {Number} index The index where the item is to be inserted
26883      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26884      * @return {Roo.Toolbar.Button/Item}
26885      */
26886     insertButton : function(index, item){
26887         if(item instanceof Array){
26888             var buttons = [];
26889             for(var i = 0, len = item.length; i < len; i++) {
26890                buttons.push(this.insertButton(index + i, item[i]));
26891             }
26892             return buttons;
26893         }
26894         if (!(item instanceof Roo.Toolbar.Button)){
26895            item = new Roo.Toolbar.Button(item);
26896         }
26897         var td = document.createElement("td");
26898         this.tr.insertBefore(td, this.tr.childNodes[index]);
26899         item.render(td);
26900         this.items.insert(index, item);
26901         return item;
26902     },
26903     
26904     /**
26905      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26906      * @param {Object} config
26907      * @return {Roo.Toolbar.Item} The element's item
26908      */
26909     addDom : function(config, returnEl){
26910         var td = this.nextBlock();
26911         Roo.DomHelper.overwrite(td, config);
26912         var ti = new Roo.Toolbar.Item(td.firstChild);
26913         ti.render(td);
26914         this.items.add(ti);
26915         return ti;
26916     },
26917
26918     /**
26919      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26920      * @type Roo.util.MixedCollection  
26921      */
26922     fields : false,
26923     
26924     /**
26925      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26926      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26927      * @param {Roo.form.Field} field
26928      * @return {Roo.ToolbarItem}
26929      */
26930      
26931       
26932     addField : function(field) {
26933         if (!this.fields) {
26934             var autoId = 0;
26935             this.fields = new Roo.util.MixedCollection(false, function(o){
26936                 return o.id || ("item" + (++autoId));
26937             });
26938
26939         }
26940         
26941         var td = this.nextBlock();
26942         field.render(td);
26943         var ti = new Roo.Toolbar.Item(td.firstChild);
26944         ti.render(td);
26945         this.items.add(ti);
26946         this.fields.add(field);
26947         return ti;
26948     },
26949     /**
26950      * Hide the toolbar
26951      * @method hide
26952      */
26953      
26954       
26955     hide : function()
26956     {
26957         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
26958         this.el.child('div').hide();
26959     },
26960     /**
26961      * Show the toolbar
26962      * @method show
26963      */
26964     show : function()
26965     {
26966         this.el.child('div').show();
26967     },
26968       
26969     // private
26970     nextBlock : function(){
26971         var td = document.createElement("td");
26972         this.tr.appendChild(td);
26973         return td;
26974     },
26975
26976     // private
26977     destroy : function(){
26978         if(this.items){ // rendered?
26979             Roo.destroy.apply(Roo, this.items.items);
26980         }
26981         if(this.fields){ // rendered?
26982             Roo.destroy.apply(Roo, this.fields.items);
26983         }
26984         Roo.Element.uncache(this.el, this.tr);
26985     }
26986 };
26987
26988 /**
26989  * @class Roo.Toolbar.Item
26990  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
26991  * @constructor
26992  * Creates a new Item
26993  * @param {HTMLElement} el 
26994  */
26995 Roo.Toolbar.Item = function(el){
26996     this.el = Roo.getDom(el);
26997     this.id = Roo.id(this.el);
26998     this.hidden = false;
26999 };
27000
27001 Roo.Toolbar.Item.prototype = {
27002     
27003     /**
27004      * Get this item's HTML Element
27005      * @return {HTMLElement}
27006      */
27007     getEl : function(){
27008        return this.el;  
27009     },
27010
27011     // private
27012     render : function(td){
27013         this.td = td;
27014         td.appendChild(this.el);
27015     },
27016     
27017     /**
27018      * Removes and destroys this item.
27019      */
27020     destroy : function(){
27021         this.td.parentNode.removeChild(this.td);
27022     },
27023     
27024     /**
27025      * Shows this item.
27026      */
27027     show: function(){
27028         this.hidden = false;
27029         this.td.style.display = "";
27030     },
27031     
27032     /**
27033      * Hides this item.
27034      */
27035     hide: function(){
27036         this.hidden = true;
27037         this.td.style.display = "none";
27038     },
27039     
27040     /**
27041      * Convenience function for boolean show/hide.
27042      * @param {Boolean} visible true to show/false to hide
27043      */
27044     setVisible: function(visible){
27045         if(visible) {
27046             this.show();
27047         }else{
27048             this.hide();
27049         }
27050     },
27051     
27052     /**
27053      * Try to focus this item.
27054      */
27055     focus : function(){
27056         Roo.fly(this.el).focus();
27057     },
27058     
27059     /**
27060      * Disables this item.
27061      */
27062     disable : function(){
27063         Roo.fly(this.td).addClass("x-item-disabled");
27064         this.disabled = true;
27065         this.el.disabled = true;
27066     },
27067     
27068     /**
27069      * Enables this item.
27070      */
27071     enable : function(){
27072         Roo.fly(this.td).removeClass("x-item-disabled");
27073         this.disabled = false;
27074         this.el.disabled = false;
27075     }
27076 };
27077
27078
27079 /**
27080  * @class Roo.Toolbar.Separator
27081  * @extends Roo.Toolbar.Item
27082  * A simple toolbar separator class
27083  * @constructor
27084  * Creates a new Separator
27085  */
27086 Roo.Toolbar.Separator = function(){
27087     var s = document.createElement("span");
27088     s.className = "ytb-sep";
27089     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27090 };
27091 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27092     enable:Roo.emptyFn,
27093     disable:Roo.emptyFn,
27094     focus:Roo.emptyFn
27095 });
27096
27097 /**
27098  * @class Roo.Toolbar.Spacer
27099  * @extends Roo.Toolbar.Item
27100  * A simple element that adds extra horizontal space to a toolbar.
27101  * @constructor
27102  * Creates a new Spacer
27103  */
27104 Roo.Toolbar.Spacer = function(){
27105     var s = document.createElement("div");
27106     s.className = "ytb-spacer";
27107     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27108 };
27109 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27110     enable:Roo.emptyFn,
27111     disable:Roo.emptyFn,
27112     focus:Roo.emptyFn
27113 });
27114
27115 /**
27116  * @class Roo.Toolbar.Fill
27117  * @extends Roo.Toolbar.Spacer
27118  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27119  * @constructor
27120  * Creates a new Spacer
27121  */
27122 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27123     // private
27124     render : function(td){
27125         td.style.width = '100%';
27126         Roo.Toolbar.Fill.superclass.render.call(this, td);
27127     }
27128 });
27129
27130 /**
27131  * @class Roo.Toolbar.TextItem
27132  * @extends Roo.Toolbar.Item
27133  * A simple class that renders text directly into a toolbar.
27134  * @constructor
27135  * Creates a new TextItem
27136  * @param {String} text
27137  */
27138 Roo.Toolbar.TextItem = function(text){
27139     if (typeof(text) == 'object') {
27140         text = text.text;
27141     }
27142     var s = document.createElement("span");
27143     s.className = "ytb-text";
27144     s.innerHTML = text;
27145     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27146 };
27147 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27148     enable:Roo.emptyFn,
27149     disable:Roo.emptyFn,
27150     focus:Roo.emptyFn
27151 });
27152
27153 /**
27154  * @class Roo.Toolbar.Button
27155  * @extends Roo.Button
27156  * A button that renders into a toolbar.
27157  * @constructor
27158  * Creates a new Button
27159  * @param {Object} config A standard {@link Roo.Button} config object
27160  */
27161 Roo.Toolbar.Button = function(config){
27162     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27163 };
27164 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27165     render : function(td){
27166         this.td = td;
27167         Roo.Toolbar.Button.superclass.render.call(this, td);
27168     },
27169     
27170     /**
27171      * Removes and destroys this button
27172      */
27173     destroy : function(){
27174         Roo.Toolbar.Button.superclass.destroy.call(this);
27175         this.td.parentNode.removeChild(this.td);
27176     },
27177     
27178     /**
27179      * Shows this button
27180      */
27181     show: function(){
27182         this.hidden = false;
27183         this.td.style.display = "";
27184     },
27185     
27186     /**
27187      * Hides this button
27188      */
27189     hide: function(){
27190         this.hidden = true;
27191         this.td.style.display = "none";
27192     },
27193
27194     /**
27195      * Disables this item
27196      */
27197     disable : function(){
27198         Roo.fly(this.td).addClass("x-item-disabled");
27199         this.disabled = true;
27200     },
27201
27202     /**
27203      * Enables this item
27204      */
27205     enable : function(){
27206         Roo.fly(this.td).removeClass("x-item-disabled");
27207         this.disabled = false;
27208     }
27209 });
27210 // backwards compat
27211 Roo.ToolbarButton = Roo.Toolbar.Button;
27212
27213 /**
27214  * @class Roo.Toolbar.SplitButton
27215  * @extends Roo.SplitButton
27216  * A menu button that renders into a toolbar.
27217  * @constructor
27218  * Creates a new SplitButton
27219  * @param {Object} config A standard {@link Roo.SplitButton} config object
27220  */
27221 Roo.Toolbar.SplitButton = function(config){
27222     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27223 };
27224 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27225     render : function(td){
27226         this.td = td;
27227         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27228     },
27229     
27230     /**
27231      * Removes and destroys this button
27232      */
27233     destroy : function(){
27234         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27235         this.td.parentNode.removeChild(this.td);
27236     },
27237     
27238     /**
27239      * Shows this button
27240      */
27241     show: function(){
27242         this.hidden = false;
27243         this.td.style.display = "";
27244     },
27245     
27246     /**
27247      * Hides this button
27248      */
27249     hide: function(){
27250         this.hidden = true;
27251         this.td.style.display = "none";
27252     }
27253 });
27254
27255 // backwards compat
27256 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27257  * Based on:
27258  * Ext JS Library 1.1.1
27259  * Copyright(c) 2006-2007, Ext JS, LLC.
27260  *
27261  * Originally Released Under LGPL - original licence link has changed is not relivant.
27262  *
27263  * Fork - LGPL
27264  * <script type="text/javascript">
27265  */
27266  
27267 /**
27268  * @class Roo.PagingToolbar
27269  * @extends Roo.Toolbar
27270  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27271  * @constructor
27272  * Create a new PagingToolbar
27273  * @param {Object} config The config object
27274  */
27275 Roo.PagingToolbar = function(el, ds, config)
27276 {
27277     // old args format still supported... - xtype is prefered..
27278     if (typeof(el) == 'object' && el.xtype) {
27279         // created from xtype...
27280         config = el;
27281         ds = el.dataSource;
27282         el = config.container;
27283     }
27284     var items = [];
27285     if (config.items) {
27286         items = config.items;
27287         config.items = [];
27288     }
27289     
27290     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27291     this.ds = ds;
27292     this.cursor = 0;
27293     this.renderButtons(this.el);
27294     this.bind(ds);
27295     
27296     // supprot items array.
27297    
27298     Roo.each(items, function(e) {
27299         this.add(Roo.factory(e));
27300     },this);
27301     
27302 };
27303
27304 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27305     /**
27306      * @cfg {Roo.data.Store} dataSource
27307      * The underlying data store providing the paged data
27308      */
27309     /**
27310      * @cfg {String/HTMLElement/Element} container
27311      * container The id or element that will contain the toolbar
27312      */
27313     /**
27314      * @cfg {Boolean} displayInfo
27315      * True to display the displayMsg (defaults to false)
27316      */
27317     /**
27318      * @cfg {Number} pageSize
27319      * The number of records to display per page (defaults to 20)
27320      */
27321     pageSize: 20,
27322     /**
27323      * @cfg {String} displayMsg
27324      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27325      */
27326     displayMsg : 'Displaying {0} - {1} of {2}',
27327     /**
27328      * @cfg {String} emptyMsg
27329      * The message to display when no records are found (defaults to "No data to display")
27330      */
27331     emptyMsg : 'No data to display',
27332     /**
27333      * Customizable piece of the default paging text (defaults to "Page")
27334      * @type String
27335      */
27336     beforePageText : "Page",
27337     /**
27338      * Customizable piece of the default paging text (defaults to "of %0")
27339      * @type String
27340      */
27341     afterPageText : "of {0}",
27342     /**
27343      * Customizable piece of the default paging text (defaults to "First Page")
27344      * @type String
27345      */
27346     firstText : "First Page",
27347     /**
27348      * Customizable piece of the default paging text (defaults to "Previous Page")
27349      * @type String
27350      */
27351     prevText : "Previous Page",
27352     /**
27353      * Customizable piece of the default paging text (defaults to "Next Page")
27354      * @type String
27355      */
27356     nextText : "Next Page",
27357     /**
27358      * Customizable piece of the default paging text (defaults to "Last Page")
27359      * @type String
27360      */
27361     lastText : "Last Page",
27362     /**
27363      * Customizable piece of the default paging text (defaults to "Refresh")
27364      * @type String
27365      */
27366     refreshText : "Refresh",
27367
27368     // private
27369     renderButtons : function(el){
27370         Roo.PagingToolbar.superclass.render.call(this, el);
27371         this.first = this.addButton({
27372             tooltip: this.firstText,
27373             cls: "x-btn-icon x-grid-page-first",
27374             disabled: true,
27375             handler: this.onClick.createDelegate(this, ["first"])
27376         });
27377         this.prev = this.addButton({
27378             tooltip: this.prevText,
27379             cls: "x-btn-icon x-grid-page-prev",
27380             disabled: true,
27381             handler: this.onClick.createDelegate(this, ["prev"])
27382         });
27383         //this.addSeparator();
27384         this.add(this.beforePageText);
27385         this.field = Roo.get(this.addDom({
27386            tag: "input",
27387            type: "text",
27388            size: "3",
27389            value: "1",
27390            cls: "x-grid-page-number"
27391         }).el);
27392         this.field.on("keydown", this.onPagingKeydown, this);
27393         this.field.on("focus", function(){this.dom.select();});
27394         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27395         this.field.setHeight(18);
27396         //this.addSeparator();
27397         this.next = this.addButton({
27398             tooltip: this.nextText,
27399             cls: "x-btn-icon x-grid-page-next",
27400             disabled: true,
27401             handler: this.onClick.createDelegate(this, ["next"])
27402         });
27403         this.last = this.addButton({
27404             tooltip: this.lastText,
27405             cls: "x-btn-icon x-grid-page-last",
27406             disabled: true,
27407             handler: this.onClick.createDelegate(this, ["last"])
27408         });
27409         //this.addSeparator();
27410         this.loading = this.addButton({
27411             tooltip: this.refreshText,
27412             cls: "x-btn-icon x-grid-loading",
27413             handler: this.onClick.createDelegate(this, ["refresh"])
27414         });
27415
27416         if(this.displayInfo){
27417             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27418         }
27419     },
27420
27421     // private
27422     updateInfo : function(){
27423         if(this.displayEl){
27424             var count = this.ds.getCount();
27425             var msg = count == 0 ?
27426                 this.emptyMsg :
27427                 String.format(
27428                     this.displayMsg,
27429                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27430                 );
27431             this.displayEl.update(msg);
27432         }
27433     },
27434
27435     // private
27436     onLoad : function(ds, r, o){
27437        this.cursor = o.params ? o.params.start : 0;
27438        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27439
27440        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27441        this.field.dom.value = ap;
27442        this.first.setDisabled(ap == 1);
27443        this.prev.setDisabled(ap == 1);
27444        this.next.setDisabled(ap == ps);
27445        this.last.setDisabled(ap == ps);
27446        this.loading.enable();
27447        this.updateInfo();
27448     },
27449
27450     // private
27451     getPageData : function(){
27452         var total = this.ds.getTotalCount();
27453         return {
27454             total : total,
27455             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27456             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27457         };
27458     },
27459
27460     // private
27461     onLoadError : function(){
27462         this.loading.enable();
27463     },
27464
27465     // private
27466     onPagingKeydown : function(e){
27467         var k = e.getKey();
27468         var d = this.getPageData();
27469         if(k == e.RETURN){
27470             var v = this.field.dom.value, pageNum;
27471             if(!v || isNaN(pageNum = parseInt(v, 10))){
27472                 this.field.dom.value = d.activePage;
27473                 return;
27474             }
27475             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27476             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27477             e.stopEvent();
27478         }
27479         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))
27480         {
27481           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27482           this.field.dom.value = pageNum;
27483           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27484           e.stopEvent();
27485         }
27486         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27487         {
27488           var v = this.field.dom.value, pageNum; 
27489           var increment = (e.shiftKey) ? 10 : 1;
27490           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27491             increment *= -1;
27492           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27493             this.field.dom.value = d.activePage;
27494             return;
27495           }
27496           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27497           {
27498             this.field.dom.value = parseInt(v, 10) + increment;
27499             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27500             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27501           }
27502           e.stopEvent();
27503         }
27504     },
27505
27506     // private
27507     beforeLoad : function(){
27508         if(this.loading){
27509             this.loading.disable();
27510         }
27511     },
27512
27513     // private
27514     onClick : function(which){
27515         var ds = this.ds;
27516         switch(which){
27517             case "first":
27518                 ds.load({params:{start: 0, limit: this.pageSize}});
27519             break;
27520             case "prev":
27521                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27522             break;
27523             case "next":
27524                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27525             break;
27526             case "last":
27527                 var total = ds.getTotalCount();
27528                 var extra = total % this.pageSize;
27529                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27530                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27531             break;
27532             case "refresh":
27533                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27534             break;
27535         }
27536     },
27537
27538     /**
27539      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27540      * @param {Roo.data.Store} store The data store to unbind
27541      */
27542     unbind : function(ds){
27543         ds.un("beforeload", this.beforeLoad, this);
27544         ds.un("load", this.onLoad, this);
27545         ds.un("loadexception", this.onLoadError, this);
27546         ds.un("remove", this.updateInfo, this);
27547         ds.un("add", this.updateInfo, this);
27548         this.ds = undefined;
27549     },
27550
27551     /**
27552      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27553      * @param {Roo.data.Store} store The data store to bind
27554      */
27555     bind : function(ds){
27556         ds.on("beforeload", this.beforeLoad, this);
27557         ds.on("load", this.onLoad, this);
27558         ds.on("loadexception", this.onLoadError, this);
27559         ds.on("remove", this.updateInfo, this);
27560         ds.on("add", this.updateInfo, this);
27561         this.ds = ds;
27562     }
27563 });/*
27564  * Based on:
27565  * Ext JS Library 1.1.1
27566  * Copyright(c) 2006-2007, Ext JS, LLC.
27567  *
27568  * Originally Released Under LGPL - original licence link has changed is not relivant.
27569  *
27570  * Fork - LGPL
27571  * <script type="text/javascript">
27572  */
27573
27574 /**
27575  * @class Roo.Resizable
27576  * @extends Roo.util.Observable
27577  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27578  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27579  * 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
27580  * the element will be wrapped for you automatically.</p>
27581  * <p>Here is the list of valid resize handles:</p>
27582  * <pre>
27583 Value   Description
27584 ------  -------------------
27585  'n'     north
27586  's'     south
27587  'e'     east
27588  'w'     west
27589  'nw'    northwest
27590  'sw'    southwest
27591  'se'    southeast
27592  'ne'    northeast
27593  'hd'    horizontal drag
27594  'all'   all
27595 </pre>
27596  * <p>Here's an example showing the creation of a typical Resizable:</p>
27597  * <pre><code>
27598 var resizer = new Roo.Resizable("element-id", {
27599     handles: 'all',
27600     minWidth: 200,
27601     minHeight: 100,
27602     maxWidth: 500,
27603     maxHeight: 400,
27604     pinned: true
27605 });
27606 resizer.on("resize", myHandler);
27607 </code></pre>
27608  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27609  * resizer.east.setDisplayed(false);</p>
27610  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27611  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27612  * resize operation's new size (defaults to [0, 0])
27613  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27614  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27615  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27616  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27617  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27618  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27619  * @cfg {Number} width The width of the element in pixels (defaults to null)
27620  * @cfg {Number} height The height of the element in pixels (defaults to null)
27621  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27622  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27623  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27624  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27625  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27626  * in favor of the handles config option (defaults to false)
27627  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27628  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27629  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27630  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27631  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27632  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27633  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27634  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27635  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27636  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27637  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27638  * @constructor
27639  * Create a new resizable component
27640  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27641  * @param {Object} config configuration options
27642   */
27643 Roo.Resizable = function(el, config)
27644 {
27645     this.el = Roo.get(el);
27646
27647     if(config && config.wrap){
27648         config.resizeChild = this.el;
27649         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27650         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27651         this.el.setStyle("overflow", "hidden");
27652         this.el.setPositioning(config.resizeChild.getPositioning());
27653         config.resizeChild.clearPositioning();
27654         if(!config.width || !config.height){
27655             var csize = config.resizeChild.getSize();
27656             this.el.setSize(csize.width, csize.height);
27657         }
27658         if(config.pinned && !config.adjustments){
27659             config.adjustments = "auto";
27660         }
27661     }
27662
27663     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27664     this.proxy.unselectable();
27665     this.proxy.enableDisplayMode('block');
27666
27667     Roo.apply(this, config);
27668
27669     if(this.pinned){
27670         this.disableTrackOver = true;
27671         this.el.addClass("x-resizable-pinned");
27672     }
27673     // if the element isn't positioned, make it relative
27674     var position = this.el.getStyle("position");
27675     if(position != "absolute" && position != "fixed"){
27676         this.el.setStyle("position", "relative");
27677     }
27678     if(!this.handles){ // no handles passed, must be legacy style
27679         this.handles = 's,e,se';
27680         if(this.multiDirectional){
27681             this.handles += ',n,w';
27682         }
27683     }
27684     if(this.handles == "all"){
27685         this.handles = "n s e w ne nw se sw";
27686     }
27687     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27688     var ps = Roo.Resizable.positions;
27689     for(var i = 0, len = hs.length; i < len; i++){
27690         if(hs[i] && ps[hs[i]]){
27691             var pos = ps[hs[i]];
27692             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27693         }
27694     }
27695     // legacy
27696     this.corner = this.southeast;
27697     
27698     // updateBox = the box can move..
27699     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27700         this.updateBox = true;
27701     }
27702
27703     this.activeHandle = null;
27704
27705     if(this.resizeChild){
27706         if(typeof this.resizeChild == "boolean"){
27707             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27708         }else{
27709             this.resizeChild = Roo.get(this.resizeChild, true);
27710         }
27711     }
27712     
27713     if(this.adjustments == "auto"){
27714         var rc = this.resizeChild;
27715         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27716         if(rc && (hw || hn)){
27717             rc.position("relative");
27718             rc.setLeft(hw ? hw.el.getWidth() : 0);
27719             rc.setTop(hn ? hn.el.getHeight() : 0);
27720         }
27721         this.adjustments = [
27722             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27723             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27724         ];
27725     }
27726
27727     if(this.draggable){
27728         this.dd = this.dynamic ?
27729             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27730         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27731     }
27732
27733     // public events
27734     this.addEvents({
27735         /**
27736          * @event beforeresize
27737          * Fired before resize is allowed. Set enabled to false to cancel resize.
27738          * @param {Roo.Resizable} this
27739          * @param {Roo.EventObject} e The mousedown event
27740          */
27741         "beforeresize" : true,
27742         /**
27743          * @event resize
27744          * Fired after a resize.
27745          * @param {Roo.Resizable} this
27746          * @param {Number} width The new width
27747          * @param {Number} height The new height
27748          * @param {Roo.EventObject} e The mouseup event
27749          */
27750         "resize" : true
27751     });
27752
27753     if(this.width !== null && this.height !== null){
27754         this.resizeTo(this.width, this.height);
27755     }else{
27756         this.updateChildSize();
27757     }
27758     if(Roo.isIE){
27759         this.el.dom.style.zoom = 1;
27760     }
27761     Roo.Resizable.superclass.constructor.call(this);
27762 };
27763
27764 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27765         resizeChild : false,
27766         adjustments : [0, 0],
27767         minWidth : 5,
27768         minHeight : 5,
27769         maxWidth : 10000,
27770         maxHeight : 10000,
27771         enabled : true,
27772         animate : false,
27773         duration : .35,
27774         dynamic : false,
27775         handles : false,
27776         multiDirectional : false,
27777         disableTrackOver : false,
27778         easing : 'easeOutStrong',
27779         widthIncrement : 0,
27780         heightIncrement : 0,
27781         pinned : false,
27782         width : null,
27783         height : null,
27784         preserveRatio : false,
27785         transparent: false,
27786         minX: 0,
27787         minY: 0,
27788         draggable: false,
27789
27790         /**
27791          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27792          */
27793         constrainTo: undefined,
27794         /**
27795          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27796          */
27797         resizeRegion: undefined,
27798
27799
27800     /**
27801      * Perform a manual resize
27802      * @param {Number} width
27803      * @param {Number} height
27804      */
27805     resizeTo : function(width, height){
27806         this.el.setSize(width, height);
27807         this.updateChildSize();
27808         this.fireEvent("resize", this, width, height, null);
27809     },
27810
27811     // private
27812     startSizing : function(e, handle){
27813         this.fireEvent("beforeresize", this, e);
27814         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27815
27816             if(!this.overlay){
27817                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27818                 this.overlay.unselectable();
27819                 this.overlay.enableDisplayMode("block");
27820                 this.overlay.on("mousemove", this.onMouseMove, this);
27821                 this.overlay.on("mouseup", this.onMouseUp, this);
27822             }
27823             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27824
27825             this.resizing = true;
27826             this.startBox = this.el.getBox();
27827             this.startPoint = e.getXY();
27828             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27829                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27830
27831             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27832             this.overlay.show();
27833
27834             if(this.constrainTo) {
27835                 var ct = Roo.get(this.constrainTo);
27836                 this.resizeRegion = ct.getRegion().adjust(
27837                     ct.getFrameWidth('t'),
27838                     ct.getFrameWidth('l'),
27839                     -ct.getFrameWidth('b'),
27840                     -ct.getFrameWidth('r')
27841                 );
27842             }
27843
27844             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27845             this.proxy.show();
27846             this.proxy.setBox(this.startBox);
27847             if(!this.dynamic){
27848                 this.proxy.setStyle('visibility', 'visible');
27849             }
27850         }
27851     },
27852
27853     // private
27854     onMouseDown : function(handle, e){
27855         if(this.enabled){
27856             e.stopEvent();
27857             this.activeHandle = handle;
27858             this.startSizing(e, handle);
27859         }
27860     },
27861
27862     // private
27863     onMouseUp : function(e){
27864         var size = this.resizeElement();
27865         this.resizing = false;
27866         this.handleOut();
27867         this.overlay.hide();
27868         this.proxy.hide();
27869         this.fireEvent("resize", this, size.width, size.height, e);
27870     },
27871
27872     // private
27873     updateChildSize : function(){
27874         if(this.resizeChild){
27875             var el = this.el;
27876             var child = this.resizeChild;
27877             var adj = this.adjustments;
27878             if(el.dom.offsetWidth){
27879                 var b = el.getSize(true);
27880                 child.setSize(b.width+adj[0], b.height+adj[1]);
27881             }
27882             // Second call here for IE
27883             // The first call enables instant resizing and
27884             // the second call corrects scroll bars if they
27885             // exist
27886             if(Roo.isIE){
27887                 setTimeout(function(){
27888                     if(el.dom.offsetWidth){
27889                         var b = el.getSize(true);
27890                         child.setSize(b.width+adj[0], b.height+adj[1]);
27891                     }
27892                 }, 10);
27893             }
27894         }
27895     },
27896
27897     // private
27898     snap : function(value, inc, min){
27899         if(!inc || !value) return value;
27900         var newValue = value;
27901         var m = value % inc;
27902         if(m > 0){
27903             if(m > (inc/2)){
27904                 newValue = value + (inc-m);
27905             }else{
27906                 newValue = value - m;
27907             }
27908         }
27909         return Math.max(min, newValue);
27910     },
27911
27912     // private
27913     resizeElement : function(){
27914         var box = this.proxy.getBox();
27915         if(this.updateBox){
27916             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27917         }else{
27918             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27919         }
27920         this.updateChildSize();
27921         if(!this.dynamic){
27922             this.proxy.hide();
27923         }
27924         return box;
27925     },
27926
27927     // private
27928     constrain : function(v, diff, m, mx){
27929         if(v - diff < m){
27930             diff = v - m;
27931         }else if(v - diff > mx){
27932             diff = mx - v;
27933         }
27934         return diff;
27935     },
27936
27937     // private
27938     onMouseMove : function(e){
27939         if(this.enabled){
27940             try{// try catch so if something goes wrong the user doesn't get hung
27941
27942             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27943                 return;
27944             }
27945
27946             //var curXY = this.startPoint;
27947             var curSize = this.curSize || this.startBox;
27948             var x = this.startBox.x, y = this.startBox.y;
27949             var ox = x, oy = y;
27950             var w = curSize.width, h = curSize.height;
27951             var ow = w, oh = h;
27952             var mw = this.minWidth, mh = this.minHeight;
27953             var mxw = this.maxWidth, mxh = this.maxHeight;
27954             var wi = this.widthIncrement;
27955             var hi = this.heightIncrement;
27956
27957             var eventXY = e.getXY();
27958             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
27959             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
27960
27961             var pos = this.activeHandle.position;
27962
27963             switch(pos){
27964                 case "east":
27965                     w += diffX;
27966                     w = Math.min(Math.max(mw, w), mxw);
27967                     break;
27968              
27969                 case "south":
27970                     h += diffY;
27971                     h = Math.min(Math.max(mh, h), mxh);
27972                     break;
27973                 case "southeast":
27974                     w += diffX;
27975                     h += diffY;
27976                     w = Math.min(Math.max(mw, w), mxw);
27977                     h = Math.min(Math.max(mh, h), mxh);
27978                     break;
27979                 case "north":
27980                     diffY = this.constrain(h, diffY, mh, mxh);
27981                     y += diffY;
27982                     h -= diffY;
27983                     break;
27984                 case "hdrag":
27985                     
27986                     if (wi) {
27987                         var adiffX = Math.abs(diffX);
27988                         var sub = (adiffX % wi); // how much 
27989                         if (sub > (wi/2)) { // far enough to snap
27990                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
27991                         } else {
27992                             // remove difference.. 
27993                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
27994                         }
27995                     }
27996                     x += diffX;
27997                     x = Math.max(this.minX, x);
27998                     break;
27999                 case "west":
28000                     diffX = this.constrain(w, diffX, mw, mxw);
28001                     x += diffX;
28002                     w -= diffX;
28003                     break;
28004                 case "northeast":
28005                     w += diffX;
28006                     w = Math.min(Math.max(mw, w), mxw);
28007                     diffY = this.constrain(h, diffY, mh, mxh);
28008                     y += diffY;
28009                     h -= diffY;
28010                     break;
28011                 case "northwest":
28012                     diffX = this.constrain(w, diffX, mw, mxw);
28013                     diffY = this.constrain(h, diffY, mh, mxh);
28014                     y += diffY;
28015                     h -= diffY;
28016                     x += diffX;
28017                     w -= diffX;
28018                     break;
28019                case "southwest":
28020                     diffX = this.constrain(w, diffX, mw, mxw);
28021                     h += diffY;
28022                     h = Math.min(Math.max(mh, h), mxh);
28023                     x += diffX;
28024                     w -= diffX;
28025                     break;
28026             }
28027
28028             var sw = this.snap(w, wi, mw);
28029             var sh = this.snap(h, hi, mh);
28030             if(sw != w || sh != h){
28031                 switch(pos){
28032                     case "northeast":
28033                         y -= sh - h;
28034                     break;
28035                     case "north":
28036                         y -= sh - h;
28037                         break;
28038                     case "southwest":
28039                         x -= sw - w;
28040                     break;
28041                     case "west":
28042                         x -= sw - w;
28043                         break;
28044                     case "northwest":
28045                         x -= sw - w;
28046                         y -= sh - h;
28047                     break;
28048                 }
28049                 w = sw;
28050                 h = sh;
28051             }
28052
28053             if(this.preserveRatio){
28054                 switch(pos){
28055                     case "southeast":
28056                     case "east":
28057                         h = oh * (w/ow);
28058                         h = Math.min(Math.max(mh, h), mxh);
28059                         w = ow * (h/oh);
28060                        break;
28061                     case "south":
28062                         w = ow * (h/oh);
28063                         w = Math.min(Math.max(mw, w), mxw);
28064                         h = oh * (w/ow);
28065                         break;
28066                     case "northeast":
28067                         w = ow * (h/oh);
28068                         w = Math.min(Math.max(mw, w), mxw);
28069                         h = oh * (w/ow);
28070                     break;
28071                     case "north":
28072                         var tw = w;
28073                         w = ow * (h/oh);
28074                         w = Math.min(Math.max(mw, w), mxw);
28075                         h = oh * (w/ow);
28076                         x += (tw - w) / 2;
28077                         break;
28078                     case "southwest":
28079                         h = oh * (w/ow);
28080                         h = Math.min(Math.max(mh, h), mxh);
28081                         var tw = w;
28082                         w = ow * (h/oh);
28083                         x += tw - w;
28084                         break;
28085                     case "west":
28086                         var th = h;
28087                         h = oh * (w/ow);
28088                         h = Math.min(Math.max(mh, h), mxh);
28089                         y += (th - h) / 2;
28090                         var tw = w;
28091                         w = ow * (h/oh);
28092                         x += tw - w;
28093                        break;
28094                     case "northwest":
28095                         var tw = w;
28096                         var th = h;
28097                         h = oh * (w/ow);
28098                         h = Math.min(Math.max(mh, h), mxh);
28099                         w = ow * (h/oh);
28100                         y += th - h;
28101                         x += tw - w;
28102                        break;
28103
28104                 }
28105             }
28106             if (pos == 'hdrag') {
28107                 w = ow;
28108             }
28109             this.proxy.setBounds(x, y, w, h);
28110             if(this.dynamic){
28111                 this.resizeElement();
28112             }
28113             }catch(e){}
28114         }
28115     },
28116
28117     // private
28118     handleOver : function(){
28119         if(this.enabled){
28120             this.el.addClass("x-resizable-over");
28121         }
28122     },
28123
28124     // private
28125     handleOut : function(){
28126         if(!this.resizing){
28127             this.el.removeClass("x-resizable-over");
28128         }
28129     },
28130
28131     /**
28132      * Returns the element this component is bound to.
28133      * @return {Roo.Element}
28134      */
28135     getEl : function(){
28136         return this.el;
28137     },
28138
28139     /**
28140      * Returns the resizeChild element (or null).
28141      * @return {Roo.Element}
28142      */
28143     getResizeChild : function(){
28144         return this.resizeChild;
28145     },
28146
28147     /**
28148      * Destroys this resizable. If the element was wrapped and
28149      * removeEl is not true then the element remains.
28150      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28151      */
28152     destroy : function(removeEl){
28153         this.proxy.remove();
28154         if(this.overlay){
28155             this.overlay.removeAllListeners();
28156             this.overlay.remove();
28157         }
28158         var ps = Roo.Resizable.positions;
28159         for(var k in ps){
28160             if(typeof ps[k] != "function" && this[ps[k]]){
28161                 var h = this[ps[k]];
28162                 h.el.removeAllListeners();
28163                 h.el.remove();
28164             }
28165         }
28166         if(removeEl){
28167             this.el.update("");
28168             this.el.remove();
28169         }
28170     }
28171 });
28172
28173 // private
28174 // hash to map config positions to true positions
28175 Roo.Resizable.positions = {
28176     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28177     hd: "hdrag"
28178 };
28179
28180 // private
28181 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28182     if(!this.tpl){
28183         // only initialize the template if resizable is used
28184         var tpl = Roo.DomHelper.createTemplate(
28185             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28186         );
28187         tpl.compile();
28188         Roo.Resizable.Handle.prototype.tpl = tpl;
28189     }
28190     this.position = pos;
28191     this.rz = rz;
28192     // show north drag fro topdra
28193     var handlepos = pos == 'hdrag' ? 'north' : pos;
28194     
28195     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28196     if (pos == 'hdrag') {
28197         this.el.setStyle('cursor', 'pointer');
28198     }
28199     this.el.unselectable();
28200     if(transparent){
28201         this.el.setOpacity(0);
28202     }
28203     this.el.on("mousedown", this.onMouseDown, this);
28204     if(!disableTrackOver){
28205         this.el.on("mouseover", this.onMouseOver, this);
28206         this.el.on("mouseout", this.onMouseOut, this);
28207     }
28208 };
28209
28210 // private
28211 Roo.Resizable.Handle.prototype = {
28212     afterResize : function(rz){
28213         // do nothing
28214     },
28215     // private
28216     onMouseDown : function(e){
28217         this.rz.onMouseDown(this, e);
28218     },
28219     // private
28220     onMouseOver : function(e){
28221         this.rz.handleOver(this, e);
28222     },
28223     // private
28224     onMouseOut : function(e){
28225         this.rz.handleOut(this, e);
28226     }
28227 };/*
28228  * Based on:
28229  * Ext JS Library 1.1.1
28230  * Copyright(c) 2006-2007, Ext JS, LLC.
28231  *
28232  * Originally Released Under LGPL - original licence link has changed is not relivant.
28233  *
28234  * Fork - LGPL
28235  * <script type="text/javascript">
28236  */
28237
28238 /**
28239  * @class Roo.Editor
28240  * @extends Roo.Component
28241  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28242  * @constructor
28243  * Create a new Editor
28244  * @param {Roo.form.Field} field The Field object (or descendant)
28245  * @param {Object} config The config object
28246  */
28247 Roo.Editor = function(field, config){
28248     Roo.Editor.superclass.constructor.call(this, config);
28249     this.field = field;
28250     this.addEvents({
28251         /**
28252              * @event beforestartedit
28253              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28254              * false from the handler of this event.
28255              * @param {Editor} this
28256              * @param {Roo.Element} boundEl The underlying element bound to this editor
28257              * @param {Mixed} value The field value being set
28258              */
28259         "beforestartedit" : true,
28260         /**
28261              * @event startedit
28262              * Fires when this editor is displayed
28263              * @param {Roo.Element} boundEl The underlying element bound to this editor
28264              * @param {Mixed} value The starting field value
28265              */
28266         "startedit" : true,
28267         /**
28268              * @event beforecomplete
28269              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28270              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28271              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28272              * event will not fire since no edit actually occurred.
28273              * @param {Editor} this
28274              * @param {Mixed} value The current field value
28275              * @param {Mixed} startValue The original field value
28276              */
28277         "beforecomplete" : true,
28278         /**
28279              * @event complete
28280              * Fires after editing is complete and any changed value has been written to the underlying field.
28281              * @param {Editor} this
28282              * @param {Mixed} value The current field value
28283              * @param {Mixed} startValue The original field value
28284              */
28285         "complete" : true,
28286         /**
28287          * @event specialkey
28288          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28289          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28290          * @param {Roo.form.Field} this
28291          * @param {Roo.EventObject} e The event object
28292          */
28293         "specialkey" : true
28294     });
28295 };
28296
28297 Roo.extend(Roo.Editor, Roo.Component, {
28298     /**
28299      * @cfg {Boolean/String} autosize
28300      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28301      * or "height" to adopt the height only (defaults to false)
28302      */
28303     /**
28304      * @cfg {Boolean} revertInvalid
28305      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28306      * validation fails (defaults to true)
28307      */
28308     /**
28309      * @cfg {Boolean} ignoreNoChange
28310      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28311      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28312      * will never be ignored.
28313      */
28314     /**
28315      * @cfg {Boolean} hideEl
28316      * False to keep the bound element visible while the editor is displayed (defaults to true)
28317      */
28318     /**
28319      * @cfg {Mixed} value
28320      * The data value of the underlying field (defaults to "")
28321      */
28322     value : "",
28323     /**
28324      * @cfg {String} alignment
28325      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28326      */
28327     alignment: "c-c?",
28328     /**
28329      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28330      * for bottom-right shadow (defaults to "frame")
28331      */
28332     shadow : "frame",
28333     /**
28334      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28335      */
28336     constrain : false,
28337     /**
28338      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28339      */
28340     completeOnEnter : false,
28341     /**
28342      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28343      */
28344     cancelOnEsc : false,
28345     /**
28346      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28347      */
28348     updateEl : false,
28349
28350     // private
28351     onRender : function(ct, position){
28352         this.el = new Roo.Layer({
28353             shadow: this.shadow,
28354             cls: "x-editor",
28355             parentEl : ct,
28356             shim : this.shim,
28357             shadowOffset:4,
28358             id: this.id,
28359             constrain: this.constrain
28360         });
28361         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28362         if(this.field.msgTarget != 'title'){
28363             this.field.msgTarget = 'qtip';
28364         }
28365         this.field.render(this.el);
28366         if(Roo.isGecko){
28367             this.field.el.dom.setAttribute('autocomplete', 'off');
28368         }
28369         this.field.on("specialkey", this.onSpecialKey, this);
28370         if(this.swallowKeys){
28371             this.field.el.swallowEvent(['keydown','keypress']);
28372         }
28373         this.field.show();
28374         this.field.on("blur", this.onBlur, this);
28375         if(this.field.grow){
28376             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28377         }
28378     },
28379
28380     onSpecialKey : function(field, e)
28381     {
28382         //Roo.log('editor onSpecialKey');
28383         if(this.completeOnEnter && e.getKey() == e.ENTER){
28384             e.stopEvent();
28385             this.completeEdit();
28386             return;
28387         }
28388         // do not fire special key otherwise it might hide close the editor...
28389         if(e.getKey() == e.ENTER){    
28390             return;
28391         }
28392         if(this.cancelOnEsc && e.getKey() == e.ESC){
28393             this.cancelEdit();
28394             return;
28395         } 
28396         this.fireEvent('specialkey', field, e);
28397     
28398     },
28399
28400     /**
28401      * Starts the editing process and shows the editor.
28402      * @param {String/HTMLElement/Element} el The element to edit
28403      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28404       * to the innerHTML of el.
28405      */
28406     startEdit : function(el, value){
28407         if(this.editing){
28408             this.completeEdit();
28409         }
28410         this.boundEl = Roo.get(el);
28411         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28412         if(!this.rendered){
28413             this.render(this.parentEl || document.body);
28414         }
28415         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28416             return;
28417         }
28418         this.startValue = v;
28419         this.field.setValue(v);
28420         if(this.autoSize){
28421             var sz = this.boundEl.getSize();
28422             switch(this.autoSize){
28423                 case "width":
28424                 this.setSize(sz.width,  "");
28425                 break;
28426                 case "height":
28427                 this.setSize("",  sz.height);
28428                 break;
28429                 default:
28430                 this.setSize(sz.width,  sz.height);
28431             }
28432         }
28433         this.el.alignTo(this.boundEl, this.alignment);
28434         this.editing = true;
28435         if(Roo.QuickTips){
28436             Roo.QuickTips.disable();
28437         }
28438         this.show();
28439     },
28440
28441     /**
28442      * Sets the height and width of this editor.
28443      * @param {Number} width The new width
28444      * @param {Number} height The new height
28445      */
28446     setSize : function(w, h){
28447         this.field.setSize(w, h);
28448         if(this.el){
28449             this.el.sync();
28450         }
28451     },
28452
28453     /**
28454      * Realigns the editor to the bound field based on the current alignment config value.
28455      */
28456     realign : function(){
28457         this.el.alignTo(this.boundEl, this.alignment);
28458     },
28459
28460     /**
28461      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28462      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28463      */
28464     completeEdit : function(remainVisible){
28465         if(!this.editing){
28466             return;
28467         }
28468         var v = this.getValue();
28469         if(this.revertInvalid !== false && !this.field.isValid()){
28470             v = this.startValue;
28471             this.cancelEdit(true);
28472         }
28473         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28474             this.editing = false;
28475             this.hide();
28476             return;
28477         }
28478         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28479             this.editing = false;
28480             if(this.updateEl && this.boundEl){
28481                 this.boundEl.update(v);
28482             }
28483             if(remainVisible !== true){
28484                 this.hide();
28485             }
28486             this.fireEvent("complete", this, v, this.startValue);
28487         }
28488     },
28489
28490     // private
28491     onShow : function(){
28492         this.el.show();
28493         if(this.hideEl !== false){
28494             this.boundEl.hide();
28495         }
28496         this.field.show();
28497         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28498             this.fixIEFocus = true;
28499             this.deferredFocus.defer(50, this);
28500         }else{
28501             this.field.focus();
28502         }
28503         this.fireEvent("startedit", this.boundEl, this.startValue);
28504     },
28505
28506     deferredFocus : function(){
28507         if(this.editing){
28508             this.field.focus();
28509         }
28510     },
28511
28512     /**
28513      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28514      * reverted to the original starting value.
28515      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28516      * cancel (defaults to false)
28517      */
28518     cancelEdit : function(remainVisible){
28519         if(this.editing){
28520             this.setValue(this.startValue);
28521             if(remainVisible !== true){
28522                 this.hide();
28523             }
28524         }
28525     },
28526
28527     // private
28528     onBlur : function(){
28529         if(this.allowBlur !== true && this.editing){
28530             this.completeEdit();
28531         }
28532     },
28533
28534     // private
28535     onHide : function(){
28536         if(this.editing){
28537             this.completeEdit();
28538             return;
28539         }
28540         this.field.blur();
28541         if(this.field.collapse){
28542             this.field.collapse();
28543         }
28544         this.el.hide();
28545         if(this.hideEl !== false){
28546             this.boundEl.show();
28547         }
28548         if(Roo.QuickTips){
28549             Roo.QuickTips.enable();
28550         }
28551     },
28552
28553     /**
28554      * Sets the data value of the editor
28555      * @param {Mixed} value Any valid value supported by the underlying field
28556      */
28557     setValue : function(v){
28558         this.field.setValue(v);
28559     },
28560
28561     /**
28562      * Gets the data value of the editor
28563      * @return {Mixed} The data value
28564      */
28565     getValue : function(){
28566         return this.field.getValue();
28567     }
28568 });/*
28569  * Based on:
28570  * Ext JS Library 1.1.1
28571  * Copyright(c) 2006-2007, Ext JS, LLC.
28572  *
28573  * Originally Released Under LGPL - original licence link has changed is not relivant.
28574  *
28575  * Fork - LGPL
28576  * <script type="text/javascript">
28577  */
28578  
28579 /**
28580  * @class Roo.BasicDialog
28581  * @extends Roo.util.Observable
28582  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28583  * <pre><code>
28584 var dlg = new Roo.BasicDialog("my-dlg", {
28585     height: 200,
28586     width: 300,
28587     minHeight: 100,
28588     minWidth: 150,
28589     modal: true,
28590     proxyDrag: true,
28591     shadow: true
28592 });
28593 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28594 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28595 dlg.addButton('Cancel', dlg.hide, dlg);
28596 dlg.show();
28597 </code></pre>
28598   <b>A Dialog should always be a direct child of the body element.</b>
28599  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28600  * @cfg {String} title Default text to display in the title bar (defaults to null)
28601  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28602  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28603  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28604  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28605  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28606  * (defaults to null with no animation)
28607  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28608  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28609  * property for valid values (defaults to 'all')
28610  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28611  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28612  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28613  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28614  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28615  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28616  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28617  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28618  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28619  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28620  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28621  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28622  * draggable = true (defaults to false)
28623  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28624  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28625  * shadow (defaults to false)
28626  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28627  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28628  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28629  * @cfg {Array} buttons Array of buttons
28630  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28631  * @constructor
28632  * Create a new BasicDialog.
28633  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28634  * @param {Object} config Configuration options
28635  */
28636 Roo.BasicDialog = function(el, config){
28637     this.el = Roo.get(el);
28638     var dh = Roo.DomHelper;
28639     if(!this.el && config && config.autoCreate){
28640         if(typeof config.autoCreate == "object"){
28641             if(!config.autoCreate.id){
28642                 config.autoCreate.id = el;
28643             }
28644             this.el = dh.append(document.body,
28645                         config.autoCreate, true);
28646         }else{
28647             this.el = dh.append(document.body,
28648                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28649         }
28650     }
28651     el = this.el;
28652     el.setDisplayed(true);
28653     el.hide = this.hideAction;
28654     this.id = el.id;
28655     el.addClass("x-dlg");
28656
28657     Roo.apply(this, config);
28658
28659     this.proxy = el.createProxy("x-dlg-proxy");
28660     this.proxy.hide = this.hideAction;
28661     this.proxy.setOpacity(.5);
28662     this.proxy.hide();
28663
28664     if(config.width){
28665         el.setWidth(config.width);
28666     }
28667     if(config.height){
28668         el.setHeight(config.height);
28669     }
28670     this.size = el.getSize();
28671     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28672         this.xy = [config.x,config.y];
28673     }else{
28674         this.xy = el.getCenterXY(true);
28675     }
28676     /** The header element @type Roo.Element */
28677     this.header = el.child("> .x-dlg-hd");
28678     /** The body element @type Roo.Element */
28679     this.body = el.child("> .x-dlg-bd");
28680     /** The footer element @type Roo.Element */
28681     this.footer = el.child("> .x-dlg-ft");
28682
28683     if(!this.header){
28684         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28685     }
28686     if(!this.body){
28687         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28688     }
28689
28690     this.header.unselectable();
28691     if(this.title){
28692         this.header.update(this.title);
28693     }
28694     // this element allows the dialog to be focused for keyboard event
28695     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28696     this.focusEl.swallowEvent("click", true);
28697
28698     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28699
28700     // wrap the body and footer for special rendering
28701     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28702     if(this.footer){
28703         this.bwrap.dom.appendChild(this.footer.dom);
28704     }
28705
28706     this.bg = this.el.createChild({
28707         tag: "div", cls:"x-dlg-bg",
28708         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28709     });
28710     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28711
28712
28713     if(this.autoScroll !== false && !this.autoTabs){
28714         this.body.setStyle("overflow", "auto");
28715     }
28716
28717     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28718
28719     if(this.closable !== false){
28720         this.el.addClass("x-dlg-closable");
28721         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28722         this.close.on("click", this.closeClick, this);
28723         this.close.addClassOnOver("x-dlg-close-over");
28724     }
28725     if(this.collapsible !== false){
28726         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28727         this.collapseBtn.on("click", this.collapseClick, this);
28728         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28729         this.header.on("dblclick", this.collapseClick, this);
28730     }
28731     if(this.resizable !== false){
28732         this.el.addClass("x-dlg-resizable");
28733         this.resizer = new Roo.Resizable(el, {
28734             minWidth: this.minWidth || 80,
28735             minHeight:this.minHeight || 80,
28736             handles: this.resizeHandles || "all",
28737             pinned: true
28738         });
28739         this.resizer.on("beforeresize", this.beforeResize, this);
28740         this.resizer.on("resize", this.onResize, this);
28741     }
28742     if(this.draggable !== false){
28743         el.addClass("x-dlg-draggable");
28744         if (!this.proxyDrag) {
28745             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28746         }
28747         else {
28748             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28749         }
28750         dd.setHandleElId(this.header.id);
28751         dd.endDrag = this.endMove.createDelegate(this);
28752         dd.startDrag = this.startMove.createDelegate(this);
28753         dd.onDrag = this.onDrag.createDelegate(this);
28754         dd.scroll = false;
28755         this.dd = dd;
28756     }
28757     if(this.modal){
28758         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28759         this.mask.enableDisplayMode("block");
28760         this.mask.hide();
28761         this.el.addClass("x-dlg-modal");
28762     }
28763     if(this.shadow){
28764         this.shadow = new Roo.Shadow({
28765             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28766             offset : this.shadowOffset
28767         });
28768     }else{
28769         this.shadowOffset = 0;
28770     }
28771     if(Roo.useShims && this.shim !== false){
28772         this.shim = this.el.createShim();
28773         this.shim.hide = this.hideAction;
28774         this.shim.hide();
28775     }else{
28776         this.shim = false;
28777     }
28778     if(this.autoTabs){
28779         this.initTabs();
28780     }
28781     if (this.buttons) { 
28782         var bts= this.buttons;
28783         this.buttons = [];
28784         Roo.each(bts, function(b) {
28785             this.addButton(b);
28786         }, this);
28787     }
28788     
28789     
28790     this.addEvents({
28791         /**
28792          * @event keydown
28793          * Fires when a key is pressed
28794          * @param {Roo.BasicDialog} this
28795          * @param {Roo.EventObject} e
28796          */
28797         "keydown" : true,
28798         /**
28799          * @event move
28800          * Fires when this dialog is moved by the user.
28801          * @param {Roo.BasicDialog} this
28802          * @param {Number} x The new page X
28803          * @param {Number} y The new page Y
28804          */
28805         "move" : true,
28806         /**
28807          * @event resize
28808          * Fires when this dialog is resized by the user.
28809          * @param {Roo.BasicDialog} this
28810          * @param {Number} width The new width
28811          * @param {Number} height The new height
28812          */
28813         "resize" : true,
28814         /**
28815          * @event beforehide
28816          * Fires before this dialog is hidden.
28817          * @param {Roo.BasicDialog} this
28818          */
28819         "beforehide" : true,
28820         /**
28821          * @event hide
28822          * Fires when this dialog is hidden.
28823          * @param {Roo.BasicDialog} this
28824          */
28825         "hide" : true,
28826         /**
28827          * @event beforeshow
28828          * Fires before this dialog is shown.
28829          * @param {Roo.BasicDialog} this
28830          */
28831         "beforeshow" : true,
28832         /**
28833          * @event show
28834          * Fires when this dialog is shown.
28835          * @param {Roo.BasicDialog} this
28836          */
28837         "show" : true
28838     });
28839     el.on("keydown", this.onKeyDown, this);
28840     el.on("mousedown", this.toFront, this);
28841     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28842     this.el.hide();
28843     Roo.DialogManager.register(this);
28844     Roo.BasicDialog.superclass.constructor.call(this);
28845 };
28846
28847 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28848     shadowOffset: Roo.isIE ? 6 : 5,
28849     minHeight: 80,
28850     minWidth: 200,
28851     minButtonWidth: 75,
28852     defaultButton: null,
28853     buttonAlign: "right",
28854     tabTag: 'div',
28855     firstShow: true,
28856
28857     /**
28858      * Sets the dialog title text
28859      * @param {String} text The title text to display
28860      * @return {Roo.BasicDialog} this
28861      */
28862     setTitle : function(text){
28863         this.header.update(text);
28864         return this;
28865     },
28866
28867     // private
28868     closeClick : function(){
28869         this.hide();
28870     },
28871
28872     // private
28873     collapseClick : function(){
28874         this[this.collapsed ? "expand" : "collapse"]();
28875     },
28876
28877     /**
28878      * Collapses the dialog to its minimized state (only the title bar is visible).
28879      * Equivalent to the user clicking the collapse dialog button.
28880      */
28881     collapse : function(){
28882         if(!this.collapsed){
28883             this.collapsed = true;
28884             this.el.addClass("x-dlg-collapsed");
28885             this.restoreHeight = this.el.getHeight();
28886             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28887         }
28888     },
28889
28890     /**
28891      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28892      * clicking the expand dialog button.
28893      */
28894     expand : function(){
28895         if(this.collapsed){
28896             this.collapsed = false;
28897             this.el.removeClass("x-dlg-collapsed");
28898             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28899         }
28900     },
28901
28902     /**
28903      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28904      * @return {Roo.TabPanel} The tabs component
28905      */
28906     initTabs : function(){
28907         var tabs = this.getTabs();
28908         while(tabs.getTab(0)){
28909             tabs.removeTab(0);
28910         }
28911         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28912             var dom = el.dom;
28913             tabs.addTab(Roo.id(dom), dom.title);
28914             dom.title = "";
28915         });
28916         tabs.activate(0);
28917         return tabs;
28918     },
28919
28920     // private
28921     beforeResize : function(){
28922         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28923     },
28924
28925     // private
28926     onResize : function(){
28927         this.refreshSize();
28928         this.syncBodyHeight();
28929         this.adjustAssets();
28930         this.focus();
28931         this.fireEvent("resize", this, this.size.width, this.size.height);
28932     },
28933
28934     // private
28935     onKeyDown : function(e){
28936         if(this.isVisible()){
28937             this.fireEvent("keydown", this, e);
28938         }
28939     },
28940
28941     /**
28942      * Resizes the dialog.
28943      * @param {Number} width
28944      * @param {Number} height
28945      * @return {Roo.BasicDialog} this
28946      */
28947     resizeTo : function(width, height){
28948         this.el.setSize(width, height);
28949         this.size = {width: width, height: height};
28950         this.syncBodyHeight();
28951         if(this.fixedcenter){
28952             this.center();
28953         }
28954         if(this.isVisible()){
28955             this.constrainXY();
28956             this.adjustAssets();
28957         }
28958         this.fireEvent("resize", this, width, height);
28959         return this;
28960     },
28961
28962
28963     /**
28964      * Resizes the dialog to fit the specified content size.
28965      * @param {Number} width
28966      * @param {Number} height
28967      * @return {Roo.BasicDialog} this
28968      */
28969     setContentSize : function(w, h){
28970         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
28971         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
28972         //if(!this.el.isBorderBox()){
28973             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
28974             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
28975         //}
28976         if(this.tabs){
28977             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
28978             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
28979         }
28980         this.resizeTo(w, h);
28981         return this;
28982     },
28983
28984     /**
28985      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
28986      * executed in response to a particular key being pressed while the dialog is active.
28987      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
28988      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
28989      * @param {Function} fn The function to call
28990      * @param {Object} scope (optional) The scope of the function
28991      * @return {Roo.BasicDialog} this
28992      */
28993     addKeyListener : function(key, fn, scope){
28994         var keyCode, shift, ctrl, alt;
28995         if(typeof key == "object" && !(key instanceof Array)){
28996             keyCode = key["key"];
28997             shift = key["shift"];
28998             ctrl = key["ctrl"];
28999             alt = key["alt"];
29000         }else{
29001             keyCode = key;
29002         }
29003         var handler = function(dlg, e){
29004             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29005                 var k = e.getKey();
29006                 if(keyCode instanceof Array){
29007                     for(var i = 0, len = keyCode.length; i < len; i++){
29008                         if(keyCode[i] == k){
29009                           fn.call(scope || window, dlg, k, e);
29010                           return;
29011                         }
29012                     }
29013                 }else{
29014                     if(k == keyCode){
29015                         fn.call(scope || window, dlg, k, e);
29016                     }
29017                 }
29018             }
29019         };
29020         this.on("keydown", handler);
29021         return this;
29022     },
29023
29024     /**
29025      * Returns the TabPanel component (creates it if it doesn't exist).
29026      * Note: If you wish to simply check for the existence of tabs without creating them,
29027      * check for a null 'tabs' property.
29028      * @return {Roo.TabPanel} The tabs component
29029      */
29030     getTabs : function(){
29031         if(!this.tabs){
29032             this.el.addClass("x-dlg-auto-tabs");
29033             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29034             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29035         }
29036         return this.tabs;
29037     },
29038
29039     /**
29040      * Adds a button to the footer section of the dialog.
29041      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29042      * object or a valid Roo.DomHelper element config
29043      * @param {Function} handler The function called when the button is clicked
29044      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29045      * @return {Roo.Button} The new button
29046      */
29047     addButton : function(config, handler, scope){
29048         var dh = Roo.DomHelper;
29049         if(!this.footer){
29050             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29051         }
29052         if(!this.btnContainer){
29053             var tb = this.footer.createChild({
29054
29055                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29056                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29057             }, null, true);
29058             this.btnContainer = tb.firstChild.firstChild.firstChild;
29059         }
29060         var bconfig = {
29061             handler: handler,
29062             scope: scope,
29063             minWidth: this.minButtonWidth,
29064             hideParent:true
29065         };
29066         if(typeof config == "string"){
29067             bconfig.text = config;
29068         }else{
29069             if(config.tag){
29070                 bconfig.dhconfig = config;
29071             }else{
29072                 Roo.apply(bconfig, config);
29073             }
29074         }
29075         var fc = false;
29076         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29077             bconfig.position = Math.max(0, bconfig.position);
29078             fc = this.btnContainer.childNodes[bconfig.position];
29079         }
29080          
29081         var btn = new Roo.Button(
29082             fc ? 
29083                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29084                 : this.btnContainer.appendChild(document.createElement("td")),
29085             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29086             bconfig
29087         );
29088         this.syncBodyHeight();
29089         if(!this.buttons){
29090             /**
29091              * Array of all the buttons that have been added to this dialog via addButton
29092              * @type Array
29093              */
29094             this.buttons = [];
29095         }
29096         this.buttons.push(btn);
29097         return btn;
29098     },
29099
29100     /**
29101      * Sets the default button to be focused when the dialog is displayed.
29102      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29103      * @return {Roo.BasicDialog} this
29104      */
29105     setDefaultButton : function(btn){
29106         this.defaultButton = btn;
29107         return this;
29108     },
29109
29110     // private
29111     getHeaderFooterHeight : function(safe){
29112         var height = 0;
29113         if(this.header){
29114            height += this.header.getHeight();
29115         }
29116         if(this.footer){
29117            var fm = this.footer.getMargins();
29118             height += (this.footer.getHeight()+fm.top+fm.bottom);
29119         }
29120         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29121         height += this.centerBg.getPadding("tb");
29122         return height;
29123     },
29124
29125     // private
29126     syncBodyHeight : function(){
29127         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29128         var height = this.size.height - this.getHeaderFooterHeight(false);
29129         bd.setHeight(height-bd.getMargins("tb"));
29130         var hh = this.header.getHeight();
29131         var h = this.size.height-hh;
29132         cb.setHeight(h);
29133         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29134         bw.setHeight(h-cb.getPadding("tb"));
29135         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29136         bd.setWidth(bw.getWidth(true));
29137         if(this.tabs){
29138             this.tabs.syncHeight();
29139             if(Roo.isIE){
29140                 this.tabs.el.repaint();
29141             }
29142         }
29143     },
29144
29145     /**
29146      * Restores the previous state of the dialog if Roo.state is configured.
29147      * @return {Roo.BasicDialog} this
29148      */
29149     restoreState : function(){
29150         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29151         if(box && box.width){
29152             this.xy = [box.x, box.y];
29153             this.resizeTo(box.width, box.height);
29154         }
29155         return this;
29156     },
29157
29158     // private
29159     beforeShow : function(){
29160         this.expand();
29161         if(this.fixedcenter){
29162             this.xy = this.el.getCenterXY(true);
29163         }
29164         if(this.modal){
29165             Roo.get(document.body).addClass("x-body-masked");
29166             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29167             this.mask.show();
29168         }
29169         this.constrainXY();
29170     },
29171
29172     // private
29173     animShow : function(){
29174         var b = Roo.get(this.animateTarget).getBox();
29175         this.proxy.setSize(b.width, b.height);
29176         this.proxy.setLocation(b.x, b.y);
29177         this.proxy.show();
29178         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29179                     true, .35, this.showEl.createDelegate(this));
29180     },
29181
29182     /**
29183      * Shows the dialog.
29184      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29185      * @return {Roo.BasicDialog} this
29186      */
29187     show : function(animateTarget){
29188         if (this.fireEvent("beforeshow", this) === false){
29189             return;
29190         }
29191         if(this.syncHeightBeforeShow){
29192             this.syncBodyHeight();
29193         }else if(this.firstShow){
29194             this.firstShow = false;
29195             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29196         }
29197         this.animateTarget = animateTarget || this.animateTarget;
29198         if(!this.el.isVisible()){
29199             this.beforeShow();
29200             if(this.animateTarget && Roo.get(this.animateTarget)){
29201                 this.animShow();
29202             }else{
29203                 this.showEl();
29204             }
29205         }
29206         return this;
29207     },
29208
29209     // private
29210     showEl : function(){
29211         this.proxy.hide();
29212         this.el.setXY(this.xy);
29213         this.el.show();
29214         this.adjustAssets(true);
29215         this.toFront();
29216         this.focus();
29217         // IE peekaboo bug - fix found by Dave Fenwick
29218         if(Roo.isIE){
29219             this.el.repaint();
29220         }
29221         this.fireEvent("show", this);
29222     },
29223
29224     /**
29225      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29226      * dialog itself will receive focus.
29227      */
29228     focus : function(){
29229         if(this.defaultButton){
29230             this.defaultButton.focus();
29231         }else{
29232             this.focusEl.focus();
29233         }
29234     },
29235
29236     // private
29237     constrainXY : function(){
29238         if(this.constraintoviewport !== false){
29239             if(!this.viewSize){
29240                 if(this.container){
29241                     var s = this.container.getSize();
29242                     this.viewSize = [s.width, s.height];
29243                 }else{
29244                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29245                 }
29246             }
29247             var s = Roo.get(this.container||document).getScroll();
29248
29249             var x = this.xy[0], y = this.xy[1];
29250             var w = this.size.width, h = this.size.height;
29251             var vw = this.viewSize[0], vh = this.viewSize[1];
29252             // only move it if it needs it
29253             var moved = false;
29254             // first validate right/bottom
29255             if(x + w > vw+s.left){
29256                 x = vw - w;
29257                 moved = true;
29258             }
29259             if(y + h > vh+s.top){
29260                 y = vh - h;
29261                 moved = true;
29262             }
29263             // then make sure top/left isn't negative
29264             if(x < s.left){
29265                 x = s.left;
29266                 moved = true;
29267             }
29268             if(y < s.top){
29269                 y = s.top;
29270                 moved = true;
29271             }
29272             if(moved){
29273                 // cache xy
29274                 this.xy = [x, y];
29275                 if(this.isVisible()){
29276                     this.el.setLocation(x, y);
29277                     this.adjustAssets();
29278                 }
29279             }
29280         }
29281     },
29282
29283     // private
29284     onDrag : function(){
29285         if(!this.proxyDrag){
29286             this.xy = this.el.getXY();
29287             this.adjustAssets();
29288         }
29289     },
29290
29291     // private
29292     adjustAssets : function(doShow){
29293         var x = this.xy[0], y = this.xy[1];
29294         var w = this.size.width, h = this.size.height;
29295         if(doShow === true){
29296             if(this.shadow){
29297                 this.shadow.show(this.el);
29298             }
29299             if(this.shim){
29300                 this.shim.show();
29301             }
29302         }
29303         if(this.shadow && this.shadow.isVisible()){
29304             this.shadow.show(this.el);
29305         }
29306         if(this.shim && this.shim.isVisible()){
29307             this.shim.setBounds(x, y, w, h);
29308         }
29309     },
29310
29311     // private
29312     adjustViewport : function(w, h){
29313         if(!w || !h){
29314             w = Roo.lib.Dom.getViewWidth();
29315             h = Roo.lib.Dom.getViewHeight();
29316         }
29317         // cache the size
29318         this.viewSize = [w, h];
29319         if(this.modal && this.mask.isVisible()){
29320             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29321             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29322         }
29323         if(this.isVisible()){
29324             this.constrainXY();
29325         }
29326     },
29327
29328     /**
29329      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29330      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29331      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29332      */
29333     destroy : function(removeEl){
29334         if(this.isVisible()){
29335             this.animateTarget = null;
29336             this.hide();
29337         }
29338         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29339         if(this.tabs){
29340             this.tabs.destroy(removeEl);
29341         }
29342         Roo.destroy(
29343              this.shim,
29344              this.proxy,
29345              this.resizer,
29346              this.close,
29347              this.mask
29348         );
29349         if(this.dd){
29350             this.dd.unreg();
29351         }
29352         if(this.buttons){
29353            for(var i = 0, len = this.buttons.length; i < len; i++){
29354                this.buttons[i].destroy();
29355            }
29356         }
29357         this.el.removeAllListeners();
29358         if(removeEl === true){
29359             this.el.update("");
29360             this.el.remove();
29361         }
29362         Roo.DialogManager.unregister(this);
29363     },
29364
29365     // private
29366     startMove : function(){
29367         if(this.proxyDrag){
29368             this.proxy.show();
29369         }
29370         if(this.constraintoviewport !== false){
29371             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29372         }
29373     },
29374
29375     // private
29376     endMove : function(){
29377         if(!this.proxyDrag){
29378             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29379         }else{
29380             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29381             this.proxy.hide();
29382         }
29383         this.refreshSize();
29384         this.adjustAssets();
29385         this.focus();
29386         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29387     },
29388
29389     /**
29390      * Brings this dialog to the front of any other visible dialogs
29391      * @return {Roo.BasicDialog} this
29392      */
29393     toFront : function(){
29394         Roo.DialogManager.bringToFront(this);
29395         return this;
29396     },
29397
29398     /**
29399      * Sends this dialog to the back (under) of any other visible dialogs
29400      * @return {Roo.BasicDialog} this
29401      */
29402     toBack : function(){
29403         Roo.DialogManager.sendToBack(this);
29404         return this;
29405     },
29406
29407     /**
29408      * Centers this dialog in the viewport
29409      * @return {Roo.BasicDialog} this
29410      */
29411     center : function(){
29412         var xy = this.el.getCenterXY(true);
29413         this.moveTo(xy[0], xy[1]);
29414         return this;
29415     },
29416
29417     /**
29418      * Moves the dialog's top-left corner to the specified point
29419      * @param {Number} x
29420      * @param {Number} y
29421      * @return {Roo.BasicDialog} this
29422      */
29423     moveTo : function(x, y){
29424         this.xy = [x,y];
29425         if(this.isVisible()){
29426             this.el.setXY(this.xy);
29427             this.adjustAssets();
29428         }
29429         return this;
29430     },
29431
29432     /**
29433      * Aligns the dialog to the specified element
29434      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29435      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29436      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29437      * @return {Roo.BasicDialog} this
29438      */
29439     alignTo : function(element, position, offsets){
29440         this.xy = this.el.getAlignToXY(element, position, offsets);
29441         if(this.isVisible()){
29442             this.el.setXY(this.xy);
29443             this.adjustAssets();
29444         }
29445         return this;
29446     },
29447
29448     /**
29449      * Anchors an element to another element and realigns it when the window is resized.
29450      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29451      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29452      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29453      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29454      * is a number, it is used as the buffer delay (defaults to 50ms).
29455      * @return {Roo.BasicDialog} this
29456      */
29457     anchorTo : function(el, alignment, offsets, monitorScroll){
29458         var action = function(){
29459             this.alignTo(el, alignment, offsets);
29460         };
29461         Roo.EventManager.onWindowResize(action, this);
29462         var tm = typeof monitorScroll;
29463         if(tm != 'undefined'){
29464             Roo.EventManager.on(window, 'scroll', action, this,
29465                 {buffer: tm == 'number' ? monitorScroll : 50});
29466         }
29467         action.call(this);
29468         return this;
29469     },
29470
29471     /**
29472      * Returns true if the dialog is visible
29473      * @return {Boolean}
29474      */
29475     isVisible : function(){
29476         return this.el.isVisible();
29477     },
29478
29479     // private
29480     animHide : function(callback){
29481         var b = Roo.get(this.animateTarget).getBox();
29482         this.proxy.show();
29483         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29484         this.el.hide();
29485         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29486                     this.hideEl.createDelegate(this, [callback]));
29487     },
29488
29489     /**
29490      * Hides the dialog.
29491      * @param {Function} callback (optional) Function to call when the dialog is hidden
29492      * @return {Roo.BasicDialog} this
29493      */
29494     hide : function(callback){
29495         if (this.fireEvent("beforehide", this) === false){
29496             return;
29497         }
29498         if(this.shadow){
29499             this.shadow.hide();
29500         }
29501         if(this.shim) {
29502           this.shim.hide();
29503         }
29504         // sometimes animateTarget seems to get set.. causing problems...
29505         // this just double checks..
29506         if(this.animateTarget && Roo.get(this.animateTarget)) {
29507            this.animHide(callback);
29508         }else{
29509             this.el.hide();
29510             this.hideEl(callback);
29511         }
29512         return this;
29513     },
29514
29515     // private
29516     hideEl : function(callback){
29517         this.proxy.hide();
29518         if(this.modal){
29519             this.mask.hide();
29520             Roo.get(document.body).removeClass("x-body-masked");
29521         }
29522         this.fireEvent("hide", this);
29523         if(typeof callback == "function"){
29524             callback();
29525         }
29526     },
29527
29528     // private
29529     hideAction : function(){
29530         this.setLeft("-10000px");
29531         this.setTop("-10000px");
29532         this.setStyle("visibility", "hidden");
29533     },
29534
29535     // private
29536     refreshSize : function(){
29537         this.size = this.el.getSize();
29538         this.xy = this.el.getXY();
29539         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29540     },
29541
29542     // private
29543     // z-index is managed by the DialogManager and may be overwritten at any time
29544     setZIndex : function(index){
29545         if(this.modal){
29546             this.mask.setStyle("z-index", index);
29547         }
29548         if(this.shim){
29549             this.shim.setStyle("z-index", ++index);
29550         }
29551         if(this.shadow){
29552             this.shadow.setZIndex(++index);
29553         }
29554         this.el.setStyle("z-index", ++index);
29555         if(this.proxy){
29556             this.proxy.setStyle("z-index", ++index);
29557         }
29558         if(this.resizer){
29559             this.resizer.proxy.setStyle("z-index", ++index);
29560         }
29561
29562         this.lastZIndex = index;
29563     },
29564
29565     /**
29566      * Returns the element for this dialog
29567      * @return {Roo.Element} The underlying dialog Element
29568      */
29569     getEl : function(){
29570         return this.el;
29571     }
29572 });
29573
29574 /**
29575  * @class Roo.DialogManager
29576  * Provides global access to BasicDialogs that have been created and
29577  * support for z-indexing (layering) multiple open dialogs.
29578  */
29579 Roo.DialogManager = function(){
29580     var list = {};
29581     var accessList = [];
29582     var front = null;
29583
29584     // private
29585     var sortDialogs = function(d1, d2){
29586         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29587     };
29588
29589     // private
29590     var orderDialogs = function(){
29591         accessList.sort(sortDialogs);
29592         var seed = Roo.DialogManager.zseed;
29593         for(var i = 0, len = accessList.length; i < len; i++){
29594             var dlg = accessList[i];
29595             if(dlg){
29596                 dlg.setZIndex(seed + (i*10));
29597             }
29598         }
29599     };
29600
29601     return {
29602         /**
29603          * The starting z-index for BasicDialogs (defaults to 9000)
29604          * @type Number The z-index value
29605          */
29606         zseed : 9000,
29607
29608         // private
29609         register : function(dlg){
29610             list[dlg.id] = dlg;
29611             accessList.push(dlg);
29612         },
29613
29614         // private
29615         unregister : function(dlg){
29616             delete list[dlg.id];
29617             var i=0;
29618             var len=0;
29619             if(!accessList.indexOf){
29620                 for(  i = 0, len = accessList.length; i < len; i++){
29621                     if(accessList[i] == dlg){
29622                         accessList.splice(i, 1);
29623                         return;
29624                     }
29625                 }
29626             }else{
29627                  i = accessList.indexOf(dlg);
29628                 if(i != -1){
29629                     accessList.splice(i, 1);
29630                 }
29631             }
29632         },
29633
29634         /**
29635          * Gets a registered dialog by id
29636          * @param {String/Object} id The id of the dialog or a dialog
29637          * @return {Roo.BasicDialog} this
29638          */
29639         get : function(id){
29640             return typeof id == "object" ? id : list[id];
29641         },
29642
29643         /**
29644          * Brings the specified dialog to the front
29645          * @param {String/Object} dlg The id of the dialog or a dialog
29646          * @return {Roo.BasicDialog} this
29647          */
29648         bringToFront : function(dlg){
29649             dlg = this.get(dlg);
29650             if(dlg != front){
29651                 front = dlg;
29652                 dlg._lastAccess = new Date().getTime();
29653                 orderDialogs();
29654             }
29655             return dlg;
29656         },
29657
29658         /**
29659          * Sends the specified dialog to the back
29660          * @param {String/Object} dlg The id of the dialog or a dialog
29661          * @return {Roo.BasicDialog} this
29662          */
29663         sendToBack : function(dlg){
29664             dlg = this.get(dlg);
29665             dlg._lastAccess = -(new Date().getTime());
29666             orderDialogs();
29667             return dlg;
29668         },
29669
29670         /**
29671          * Hides all dialogs
29672          */
29673         hideAll : function(){
29674             for(var id in list){
29675                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29676                     list[id].hide();
29677                 }
29678             }
29679         }
29680     };
29681 }();
29682
29683 /**
29684  * @class Roo.LayoutDialog
29685  * @extends Roo.BasicDialog
29686  * Dialog which provides adjustments for working with a layout in a Dialog.
29687  * Add your necessary layout config options to the dialog's config.<br>
29688  * Example usage (including a nested layout):
29689  * <pre><code>
29690 if(!dialog){
29691     dialog = new Roo.LayoutDialog("download-dlg", {
29692         modal: true,
29693         width:600,
29694         height:450,
29695         shadow:true,
29696         minWidth:500,
29697         minHeight:350,
29698         autoTabs:true,
29699         proxyDrag:true,
29700         // layout config merges with the dialog config
29701         center:{
29702             tabPosition: "top",
29703             alwaysShowTabs: true
29704         }
29705     });
29706     dialog.addKeyListener(27, dialog.hide, dialog);
29707     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29708     dialog.addButton("Build It!", this.getDownload, this);
29709
29710     // we can even add nested layouts
29711     var innerLayout = new Roo.BorderLayout("dl-inner", {
29712         east: {
29713             initialSize: 200,
29714             autoScroll:true,
29715             split:true
29716         },
29717         center: {
29718             autoScroll:true
29719         }
29720     });
29721     innerLayout.beginUpdate();
29722     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29723     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29724     innerLayout.endUpdate(true);
29725
29726     var layout = dialog.getLayout();
29727     layout.beginUpdate();
29728     layout.add("center", new Roo.ContentPanel("standard-panel",
29729                         {title: "Download the Source", fitToFrame:true}));
29730     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29731                {title: "Build your own roo.js"}));
29732     layout.getRegion("center").showPanel(sp);
29733     layout.endUpdate();
29734 }
29735 </code></pre>
29736     * @constructor
29737     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29738     * @param {Object} config configuration options
29739   */
29740 Roo.LayoutDialog = function(el, cfg){
29741     
29742     var config=  cfg;
29743     if (typeof(cfg) == 'undefined') {
29744         config = Roo.apply({}, el);
29745         // not sure why we use documentElement here.. - it should always be body.
29746         // IE7 borks horribly if we use documentElement.
29747         // webkit also does not like documentElement - it creates a body element...
29748         el = Roo.get( document.body || document.documentElement ).createChild();
29749         //config.autoCreate = true;
29750     }
29751     
29752     
29753     config.autoTabs = false;
29754     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29755     this.body.setStyle({overflow:"hidden", position:"relative"});
29756     this.layout = new Roo.BorderLayout(this.body.dom, config);
29757     this.layout.monitorWindowResize = false;
29758     this.el.addClass("x-dlg-auto-layout");
29759     // fix case when center region overwrites center function
29760     this.center = Roo.BasicDialog.prototype.center;
29761     this.on("show", this.layout.layout, this.layout, true);
29762     if (config.items) {
29763         var xitems = config.items;
29764         delete config.items;
29765         Roo.each(xitems, this.addxtype, this);
29766     }
29767     
29768     
29769 };
29770 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29771     /**
29772      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29773      * @deprecated
29774      */
29775     endUpdate : function(){
29776         this.layout.endUpdate();
29777     },
29778
29779     /**
29780      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29781      *  @deprecated
29782      */
29783     beginUpdate : function(){
29784         this.layout.beginUpdate();
29785     },
29786
29787     /**
29788      * Get the BorderLayout for this dialog
29789      * @return {Roo.BorderLayout}
29790      */
29791     getLayout : function(){
29792         return this.layout;
29793     },
29794
29795     showEl : function(){
29796         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29797         if(Roo.isIE7){
29798             this.layout.layout();
29799         }
29800     },
29801
29802     // private
29803     // Use the syncHeightBeforeShow config option to control this automatically
29804     syncBodyHeight : function(){
29805         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29806         if(this.layout){this.layout.layout();}
29807     },
29808     
29809       /**
29810      * Add an xtype element (actually adds to the layout.)
29811      * @return {Object} xdata xtype object data.
29812      */
29813     
29814     addxtype : function(c) {
29815         return this.layout.addxtype(c);
29816     }
29817 });/*
29818  * Based on:
29819  * Ext JS Library 1.1.1
29820  * Copyright(c) 2006-2007, Ext JS, LLC.
29821  *
29822  * Originally Released Under LGPL - original licence link has changed is not relivant.
29823  *
29824  * Fork - LGPL
29825  * <script type="text/javascript">
29826  */
29827  
29828 /**
29829  * @class Roo.MessageBox
29830  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29831  * Example usage:
29832  *<pre><code>
29833 // Basic alert:
29834 Roo.Msg.alert('Status', 'Changes saved successfully.');
29835
29836 // Prompt for user data:
29837 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29838     if (btn == 'ok'){
29839         // process text value...
29840     }
29841 });
29842
29843 // Show a dialog using config options:
29844 Roo.Msg.show({
29845    title:'Save Changes?',
29846    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29847    buttons: Roo.Msg.YESNOCANCEL,
29848    fn: processResult,
29849    animEl: 'elId'
29850 });
29851 </code></pre>
29852  * @singleton
29853  */
29854 Roo.MessageBox = function(){
29855     var dlg, opt, mask, waitTimer;
29856     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29857     var buttons, activeTextEl, bwidth;
29858
29859     // private
29860     var handleButton = function(button){
29861         dlg.hide();
29862         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29863     };
29864
29865     // private
29866     var handleHide = function(){
29867         if(opt && opt.cls){
29868             dlg.el.removeClass(opt.cls);
29869         }
29870         if(waitTimer){
29871             Roo.TaskMgr.stop(waitTimer);
29872             waitTimer = null;
29873         }
29874     };
29875
29876     // private
29877     var updateButtons = function(b){
29878         var width = 0;
29879         if(!b){
29880             buttons["ok"].hide();
29881             buttons["cancel"].hide();
29882             buttons["yes"].hide();
29883             buttons["no"].hide();
29884             dlg.footer.dom.style.display = 'none';
29885             return width;
29886         }
29887         dlg.footer.dom.style.display = '';
29888         for(var k in buttons){
29889             if(typeof buttons[k] != "function"){
29890                 if(b[k]){
29891                     buttons[k].show();
29892                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29893                     width += buttons[k].el.getWidth()+15;
29894                 }else{
29895                     buttons[k].hide();
29896                 }
29897             }
29898         }
29899         return width;
29900     };
29901
29902     // private
29903     var handleEsc = function(d, k, e){
29904         if(opt && opt.closable !== false){
29905             dlg.hide();
29906         }
29907         if(e){
29908             e.stopEvent();
29909         }
29910     };
29911
29912     return {
29913         /**
29914          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29915          * @return {Roo.BasicDialog} The BasicDialog element
29916          */
29917         getDialog : function(){
29918            if(!dlg){
29919                 dlg = new Roo.BasicDialog("x-msg-box", {
29920                     autoCreate : true,
29921                     shadow: true,
29922                     draggable: true,
29923                     resizable:false,
29924                     constraintoviewport:false,
29925                     fixedcenter:true,
29926                     collapsible : false,
29927                     shim:true,
29928                     modal: true,
29929                     width:400, height:100,
29930                     buttonAlign:"center",
29931                     closeClick : function(){
29932                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
29933                             handleButton("no");
29934                         }else{
29935                             handleButton("cancel");
29936                         }
29937                     }
29938                 });
29939                 dlg.on("hide", handleHide);
29940                 mask = dlg.mask;
29941                 dlg.addKeyListener(27, handleEsc);
29942                 buttons = {};
29943                 var bt = this.buttonText;
29944                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
29945                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
29946                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
29947                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
29948                 bodyEl = dlg.body.createChild({
29949
29950                     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>'
29951                 });
29952                 msgEl = bodyEl.dom.firstChild;
29953                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
29954                 textboxEl.enableDisplayMode();
29955                 textboxEl.addKeyListener([10,13], function(){
29956                     if(dlg.isVisible() && opt && opt.buttons){
29957                         if(opt.buttons.ok){
29958                             handleButton("ok");
29959                         }else if(opt.buttons.yes){
29960                             handleButton("yes");
29961                         }
29962                     }
29963                 });
29964                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
29965                 textareaEl.enableDisplayMode();
29966                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
29967                 progressEl.enableDisplayMode();
29968                 var pf = progressEl.dom.firstChild;
29969                 if (pf) {
29970                     pp = Roo.get(pf.firstChild);
29971                     pp.setHeight(pf.offsetHeight);
29972                 }
29973                 
29974             }
29975             return dlg;
29976         },
29977
29978         /**
29979          * Updates the message box body text
29980          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
29981          * the XHTML-compliant non-breaking space character '&amp;#160;')
29982          * @return {Roo.MessageBox} This message box
29983          */
29984         updateText : function(text){
29985             if(!dlg.isVisible() && !opt.width){
29986                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
29987             }
29988             msgEl.innerHTML = text || '&#160;';
29989             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
29990                         Math.max(opt.minWidth || this.minWidth, bwidth));
29991             if(opt.prompt){
29992                 activeTextEl.setWidth(w);
29993             }
29994             if(dlg.isVisible()){
29995                 dlg.fixedcenter = false;
29996             }
29997             dlg.setContentSize(w, bodyEl.getHeight());
29998             if(dlg.isVisible()){
29999                 dlg.fixedcenter = true;
30000             }
30001             return this;
30002         },
30003
30004         /**
30005          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30006          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30007          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30008          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30009          * @return {Roo.MessageBox} This message box
30010          */
30011         updateProgress : function(value, text){
30012             if(text){
30013                 this.updateText(text);
30014             }
30015             if (pp) { // weird bug on my firefox - for some reason this is not defined
30016                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30017             }
30018             return this;
30019         },        
30020
30021         /**
30022          * Returns true if the message box is currently displayed
30023          * @return {Boolean} True if the message box is visible, else false
30024          */
30025         isVisible : function(){
30026             return dlg && dlg.isVisible();  
30027         },
30028
30029         /**
30030          * Hides the message box if it is displayed
30031          */
30032         hide : function(){
30033             if(this.isVisible()){
30034                 dlg.hide();
30035             }  
30036         },
30037
30038         /**
30039          * Displays a new message box, or reinitializes an existing message box, based on the config options
30040          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30041          * The following config object properties are supported:
30042          * <pre>
30043 Property    Type             Description
30044 ----------  ---------------  ------------------------------------------------------------------------------------
30045 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30046                                    closes (defaults to undefined)
30047 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30048                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30049 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30050                                    progress and wait dialogs will ignore this property and always hide the
30051                                    close button as they can only be closed programmatically.
30052 cls               String           A custom CSS class to apply to the message box element
30053 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30054                                    displayed (defaults to 75)
30055 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30056                                    function will be btn (the name of the button that was clicked, if applicable,
30057                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30058                                    Progress and wait dialogs will ignore this option since they do not respond to
30059                                    user actions and can only be closed programmatically, so any required function
30060                                    should be called by the same code after it closes the dialog.
30061 icon              String           A CSS class that provides a background image to be used as an icon for
30062                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30063 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30064 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30065 modal             Boolean          False to allow user interaction with the page while the message box is
30066                                    displayed (defaults to true)
30067 msg               String           A string that will replace the existing message box body text (defaults
30068                                    to the XHTML-compliant non-breaking space character '&#160;')
30069 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30070 progress          Boolean          True to display a progress bar (defaults to false)
30071 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30072 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30073 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30074 title             String           The title text
30075 value             String           The string value to set into the active textbox element if displayed
30076 wait              Boolean          True to display a progress bar (defaults to false)
30077 width             Number           The width of the dialog in pixels
30078 </pre>
30079          *
30080          * Example usage:
30081          * <pre><code>
30082 Roo.Msg.show({
30083    title: 'Address',
30084    msg: 'Please enter your address:',
30085    width: 300,
30086    buttons: Roo.MessageBox.OKCANCEL,
30087    multiline: true,
30088    fn: saveAddress,
30089    animEl: 'addAddressBtn'
30090 });
30091 </code></pre>
30092          * @param {Object} config Configuration options
30093          * @return {Roo.MessageBox} This message box
30094          */
30095         show : function(options){
30096             if(this.isVisible()){
30097                 this.hide();
30098             }
30099             var d = this.getDialog();
30100             opt = options;
30101             d.setTitle(opt.title || "&#160;");
30102             d.close.setDisplayed(opt.closable !== false);
30103             activeTextEl = textboxEl;
30104             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30105             if(opt.prompt){
30106                 if(opt.multiline){
30107                     textboxEl.hide();
30108                     textareaEl.show();
30109                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30110                         opt.multiline : this.defaultTextHeight);
30111                     activeTextEl = textareaEl;
30112                 }else{
30113                     textboxEl.show();
30114                     textareaEl.hide();
30115                 }
30116             }else{
30117                 textboxEl.hide();
30118                 textareaEl.hide();
30119             }
30120             progressEl.setDisplayed(opt.progress === true);
30121             this.updateProgress(0);
30122             activeTextEl.dom.value = opt.value || "";
30123             if(opt.prompt){
30124                 dlg.setDefaultButton(activeTextEl);
30125             }else{
30126                 var bs = opt.buttons;
30127                 var db = null;
30128                 if(bs && bs.ok){
30129                     db = buttons["ok"];
30130                 }else if(bs && bs.yes){
30131                     db = buttons["yes"];
30132                 }
30133                 dlg.setDefaultButton(db);
30134             }
30135             bwidth = updateButtons(opt.buttons);
30136             this.updateText(opt.msg);
30137             if(opt.cls){
30138                 d.el.addClass(opt.cls);
30139             }
30140             d.proxyDrag = opt.proxyDrag === true;
30141             d.modal = opt.modal !== false;
30142             d.mask = opt.modal !== false ? mask : false;
30143             if(!d.isVisible()){
30144                 // force it to the end of the z-index stack so it gets a cursor in FF
30145                 document.body.appendChild(dlg.el.dom);
30146                 d.animateTarget = null;
30147                 d.show(options.animEl);
30148             }
30149             return this;
30150         },
30151
30152         /**
30153          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30154          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30155          * and closing the message box when the process is complete.
30156          * @param {String} title The title bar text
30157          * @param {String} msg The message box body text
30158          * @return {Roo.MessageBox} This message box
30159          */
30160         progress : function(title, msg){
30161             this.show({
30162                 title : title,
30163                 msg : msg,
30164                 buttons: false,
30165                 progress:true,
30166                 closable:false,
30167                 minWidth: this.minProgressWidth,
30168                 modal : true
30169             });
30170             return this;
30171         },
30172
30173         /**
30174          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30175          * If a callback function is passed it will be called after the user clicks the button, and the
30176          * id of the button that was clicked will be passed as the only parameter to the callback
30177          * (could also be the top-right close button).
30178          * @param {String} title The title bar text
30179          * @param {String} msg The message box body text
30180          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30181          * @param {Object} scope (optional) The scope of the callback function
30182          * @return {Roo.MessageBox} This message box
30183          */
30184         alert : function(title, msg, fn, scope){
30185             this.show({
30186                 title : title,
30187                 msg : msg,
30188                 buttons: this.OK,
30189                 fn: fn,
30190                 scope : scope,
30191                 modal : true
30192             });
30193             return this;
30194         },
30195
30196         /**
30197          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30198          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30199          * You are responsible for closing the message box when the process is complete.
30200          * @param {String} msg The message box body text
30201          * @param {String} title (optional) The title bar text
30202          * @return {Roo.MessageBox} This message box
30203          */
30204         wait : function(msg, title){
30205             this.show({
30206                 title : title,
30207                 msg : msg,
30208                 buttons: false,
30209                 closable:false,
30210                 progress:true,
30211                 modal:true,
30212                 width:300,
30213                 wait:true
30214             });
30215             waitTimer = Roo.TaskMgr.start({
30216                 run: function(i){
30217                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30218                 },
30219                 interval: 1000
30220             });
30221             return this;
30222         },
30223
30224         /**
30225          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30226          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30227          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30228          * @param {String} title The title bar text
30229          * @param {String} msg The message box body text
30230          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30231          * @param {Object} scope (optional) The scope of the callback function
30232          * @return {Roo.MessageBox} This message box
30233          */
30234         confirm : function(title, msg, fn, scope){
30235             this.show({
30236                 title : title,
30237                 msg : msg,
30238                 buttons: this.YESNO,
30239                 fn: fn,
30240                 scope : scope,
30241                 modal : true
30242             });
30243             return this;
30244         },
30245
30246         /**
30247          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30248          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30249          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30250          * (could also be the top-right close button) and the text that was entered will be passed as the two
30251          * parameters to the callback.
30252          * @param {String} title The title bar text
30253          * @param {String} msg The message box body text
30254          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30255          * @param {Object} scope (optional) The scope of the callback function
30256          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30257          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30258          * @return {Roo.MessageBox} This message box
30259          */
30260         prompt : function(title, msg, fn, scope, multiline){
30261             this.show({
30262                 title : title,
30263                 msg : msg,
30264                 buttons: this.OKCANCEL,
30265                 fn: fn,
30266                 minWidth:250,
30267                 scope : scope,
30268                 prompt:true,
30269                 multiline: multiline,
30270                 modal : true
30271             });
30272             return this;
30273         },
30274
30275         /**
30276          * Button config that displays a single OK button
30277          * @type Object
30278          */
30279         OK : {ok:true},
30280         /**
30281          * Button config that displays Yes and No buttons
30282          * @type Object
30283          */
30284         YESNO : {yes:true, no:true},
30285         /**
30286          * Button config that displays OK and Cancel buttons
30287          * @type Object
30288          */
30289         OKCANCEL : {ok:true, cancel:true},
30290         /**
30291          * Button config that displays Yes, No and Cancel buttons
30292          * @type Object
30293          */
30294         YESNOCANCEL : {yes:true, no:true, cancel:true},
30295
30296         /**
30297          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30298          * @type Number
30299          */
30300         defaultTextHeight : 75,
30301         /**
30302          * The maximum width in pixels of the message box (defaults to 600)
30303          * @type Number
30304          */
30305         maxWidth : 600,
30306         /**
30307          * The minimum width in pixels of the message box (defaults to 100)
30308          * @type Number
30309          */
30310         minWidth : 100,
30311         /**
30312          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30313          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30314          * @type Number
30315          */
30316         minProgressWidth : 250,
30317         /**
30318          * An object containing the default button text strings that can be overriden for localized language support.
30319          * Supported properties are: ok, cancel, yes and no.
30320          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30321          * @type Object
30322          */
30323         buttonText : {
30324             ok : "OK",
30325             cancel : "Cancel",
30326             yes : "Yes",
30327             no : "No"
30328         }
30329     };
30330 }();
30331
30332 /**
30333  * Shorthand for {@link Roo.MessageBox}
30334  */
30335 Roo.Msg = Roo.MessageBox;/*
30336  * Based on:
30337  * Ext JS Library 1.1.1
30338  * Copyright(c) 2006-2007, Ext JS, LLC.
30339  *
30340  * Originally Released Under LGPL - original licence link has changed is not relivant.
30341  *
30342  * Fork - LGPL
30343  * <script type="text/javascript">
30344  */
30345 /**
30346  * @class Roo.QuickTips
30347  * Provides attractive and customizable tooltips for any element.
30348  * @singleton
30349  */
30350 Roo.QuickTips = function(){
30351     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30352     var ce, bd, xy, dd;
30353     var visible = false, disabled = true, inited = false;
30354     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30355     
30356     var onOver = function(e){
30357         if(disabled){
30358             return;
30359         }
30360         var t = e.getTarget();
30361         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30362             return;
30363         }
30364         if(ce && t == ce.el){
30365             clearTimeout(hideProc);
30366             return;
30367         }
30368         if(t && tagEls[t.id]){
30369             tagEls[t.id].el = t;
30370             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30371             return;
30372         }
30373         var ttp, et = Roo.fly(t);
30374         var ns = cfg.namespace;
30375         if(tm.interceptTitles && t.title){
30376             ttp = t.title;
30377             t.qtip = ttp;
30378             t.removeAttribute("title");
30379             e.preventDefault();
30380         }else{
30381             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30382         }
30383         if(ttp){
30384             showProc = show.defer(tm.showDelay, tm, [{
30385                 el: t, 
30386                 text: ttp, 
30387                 width: et.getAttributeNS(ns, cfg.width),
30388                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30389                 title: et.getAttributeNS(ns, cfg.title),
30390                     cls: et.getAttributeNS(ns, cfg.cls)
30391             }]);
30392         }
30393     };
30394     
30395     var onOut = function(e){
30396         clearTimeout(showProc);
30397         var t = e.getTarget();
30398         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30399             hideProc = setTimeout(hide, tm.hideDelay);
30400         }
30401     };
30402     
30403     var onMove = function(e){
30404         if(disabled){
30405             return;
30406         }
30407         xy = e.getXY();
30408         xy[1] += 18;
30409         if(tm.trackMouse && ce){
30410             el.setXY(xy);
30411         }
30412     };
30413     
30414     var onDown = function(e){
30415         clearTimeout(showProc);
30416         clearTimeout(hideProc);
30417         if(!e.within(el)){
30418             if(tm.hideOnClick){
30419                 hide();
30420                 tm.disable();
30421                 tm.enable.defer(100, tm);
30422             }
30423         }
30424     };
30425     
30426     var getPad = function(){
30427         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30428     };
30429
30430     var show = function(o){
30431         if(disabled){
30432             return;
30433         }
30434         clearTimeout(dismissProc);
30435         ce = o;
30436         if(removeCls){ // in case manually hidden
30437             el.removeClass(removeCls);
30438             removeCls = null;
30439         }
30440         if(ce.cls){
30441             el.addClass(ce.cls);
30442             removeCls = ce.cls;
30443         }
30444         if(ce.title){
30445             tipTitle.update(ce.title);
30446             tipTitle.show();
30447         }else{
30448             tipTitle.update('');
30449             tipTitle.hide();
30450         }
30451         el.dom.style.width  = tm.maxWidth+'px';
30452         //tipBody.dom.style.width = '';
30453         tipBodyText.update(o.text);
30454         var p = getPad(), w = ce.width;
30455         if(!w){
30456             var td = tipBodyText.dom;
30457             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30458             if(aw > tm.maxWidth){
30459                 w = tm.maxWidth;
30460             }else if(aw < tm.minWidth){
30461                 w = tm.minWidth;
30462             }else{
30463                 w = aw;
30464             }
30465         }
30466         //tipBody.setWidth(w);
30467         el.setWidth(parseInt(w, 10) + p);
30468         if(ce.autoHide === false){
30469             close.setDisplayed(true);
30470             if(dd){
30471                 dd.unlock();
30472             }
30473         }else{
30474             close.setDisplayed(false);
30475             if(dd){
30476                 dd.lock();
30477             }
30478         }
30479         if(xy){
30480             el.avoidY = xy[1]-18;
30481             el.setXY(xy);
30482         }
30483         if(tm.animate){
30484             el.setOpacity(.1);
30485             el.setStyle("visibility", "visible");
30486             el.fadeIn({callback: afterShow});
30487         }else{
30488             afterShow();
30489         }
30490     };
30491     
30492     var afterShow = function(){
30493         if(ce){
30494             el.show();
30495             esc.enable();
30496             if(tm.autoDismiss && ce.autoHide !== false){
30497                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30498             }
30499         }
30500     };
30501     
30502     var hide = function(noanim){
30503         clearTimeout(dismissProc);
30504         clearTimeout(hideProc);
30505         ce = null;
30506         if(el.isVisible()){
30507             esc.disable();
30508             if(noanim !== true && tm.animate){
30509                 el.fadeOut({callback: afterHide});
30510             }else{
30511                 afterHide();
30512             } 
30513         }
30514     };
30515     
30516     var afterHide = function(){
30517         el.hide();
30518         if(removeCls){
30519             el.removeClass(removeCls);
30520             removeCls = null;
30521         }
30522     };
30523     
30524     return {
30525         /**
30526         * @cfg {Number} minWidth
30527         * The minimum width of the quick tip (defaults to 40)
30528         */
30529        minWidth : 40,
30530         /**
30531         * @cfg {Number} maxWidth
30532         * The maximum width of the quick tip (defaults to 300)
30533         */
30534        maxWidth : 300,
30535         /**
30536         * @cfg {Boolean} interceptTitles
30537         * True to automatically use the element's DOM title value if available (defaults to false)
30538         */
30539        interceptTitles : false,
30540         /**
30541         * @cfg {Boolean} trackMouse
30542         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30543         */
30544        trackMouse : false,
30545         /**
30546         * @cfg {Boolean} hideOnClick
30547         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30548         */
30549        hideOnClick : true,
30550         /**
30551         * @cfg {Number} showDelay
30552         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30553         */
30554        showDelay : 500,
30555         /**
30556         * @cfg {Number} hideDelay
30557         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30558         */
30559        hideDelay : 200,
30560         /**
30561         * @cfg {Boolean} autoHide
30562         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30563         * Used in conjunction with hideDelay.
30564         */
30565        autoHide : true,
30566         /**
30567         * @cfg {Boolean}
30568         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30569         * (defaults to true).  Used in conjunction with autoDismissDelay.
30570         */
30571        autoDismiss : true,
30572         /**
30573         * @cfg {Number}
30574         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30575         */
30576        autoDismissDelay : 5000,
30577        /**
30578         * @cfg {Boolean} animate
30579         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30580         */
30581        animate : false,
30582
30583        /**
30584         * @cfg {String} title
30585         * Title text to display (defaults to '').  This can be any valid HTML markup.
30586         */
30587         title: '',
30588        /**
30589         * @cfg {String} text
30590         * Body text to display (defaults to '').  This can be any valid HTML markup.
30591         */
30592         text : '',
30593        /**
30594         * @cfg {String} cls
30595         * A CSS class to apply to the base quick tip element (defaults to '').
30596         */
30597         cls : '',
30598        /**
30599         * @cfg {Number} width
30600         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30601         * minWidth or maxWidth.
30602         */
30603         width : null,
30604
30605     /**
30606      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30607      * or display QuickTips in a page.
30608      */
30609        init : function(){
30610           tm = Roo.QuickTips;
30611           cfg = tm.tagConfig;
30612           if(!inited){
30613               if(!Roo.isReady){ // allow calling of init() before onReady
30614                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30615                   return;
30616               }
30617               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30618               el.fxDefaults = {stopFx: true};
30619               // maximum custom styling
30620               //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>');
30621               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>');              
30622               tipTitle = el.child('h3');
30623               tipTitle.enableDisplayMode("block");
30624               tipBody = el.child('div.x-tip-bd');
30625               tipBodyText = el.child('div.x-tip-bd-inner');
30626               //bdLeft = el.child('div.x-tip-bd-left');
30627               //bdRight = el.child('div.x-tip-bd-right');
30628               close = el.child('div.x-tip-close');
30629               close.enableDisplayMode("block");
30630               close.on("click", hide);
30631               var d = Roo.get(document);
30632               d.on("mousedown", onDown);
30633               d.on("mouseover", onOver);
30634               d.on("mouseout", onOut);
30635               d.on("mousemove", onMove);
30636               esc = d.addKeyListener(27, hide);
30637               esc.disable();
30638               if(Roo.dd.DD){
30639                   dd = el.initDD("default", null, {
30640                       onDrag : function(){
30641                           el.sync();  
30642                       }
30643                   });
30644                   dd.setHandleElId(tipTitle.id);
30645                   dd.lock();
30646               }
30647               inited = true;
30648           }
30649           this.enable(); 
30650        },
30651
30652     /**
30653      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30654      * are supported:
30655      * <pre>
30656 Property    Type                   Description
30657 ----------  ---------------------  ------------------------------------------------------------------------
30658 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30659      * </ul>
30660      * @param {Object} config The config object
30661      */
30662        register : function(config){
30663            var cs = config instanceof Array ? config : arguments;
30664            for(var i = 0, len = cs.length; i < len; i++) {
30665                var c = cs[i];
30666                var target = c.target;
30667                if(target){
30668                    if(target instanceof Array){
30669                        for(var j = 0, jlen = target.length; j < jlen; j++){
30670                            tagEls[target[j]] = c;
30671                        }
30672                    }else{
30673                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30674                    }
30675                }
30676            }
30677        },
30678
30679     /**
30680      * Removes this quick tip from its element and destroys it.
30681      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30682      */
30683        unregister : function(el){
30684            delete tagEls[Roo.id(el)];
30685        },
30686
30687     /**
30688      * Enable this quick tip.
30689      */
30690        enable : function(){
30691            if(inited && disabled){
30692                locks.pop();
30693                if(locks.length < 1){
30694                    disabled = false;
30695                }
30696            }
30697        },
30698
30699     /**
30700      * Disable this quick tip.
30701      */
30702        disable : function(){
30703           disabled = true;
30704           clearTimeout(showProc);
30705           clearTimeout(hideProc);
30706           clearTimeout(dismissProc);
30707           if(ce){
30708               hide(true);
30709           }
30710           locks.push(1);
30711        },
30712
30713     /**
30714      * Returns true if the quick tip is enabled, else false.
30715      */
30716        isEnabled : function(){
30717             return !disabled;
30718        },
30719
30720         // private
30721        tagConfig : {
30722            namespace : "ext",
30723            attribute : "qtip",
30724            width : "width",
30725            target : "target",
30726            title : "qtitle",
30727            hide : "hide",
30728            cls : "qclass"
30729        }
30730    };
30731 }();
30732
30733 // backwards compat
30734 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30735  * Based on:
30736  * Ext JS Library 1.1.1
30737  * Copyright(c) 2006-2007, Ext JS, LLC.
30738  *
30739  * Originally Released Under LGPL - original licence link has changed is not relivant.
30740  *
30741  * Fork - LGPL
30742  * <script type="text/javascript">
30743  */
30744  
30745
30746 /**
30747  * @class Roo.tree.TreePanel
30748  * @extends Roo.data.Tree
30749
30750  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30751  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30752  * @cfg {Boolean} enableDD true to enable drag and drop
30753  * @cfg {Boolean} enableDrag true to enable just drag
30754  * @cfg {Boolean} enableDrop true to enable just drop
30755  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30756  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30757  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30758  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30759  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30760  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30761  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30762  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30763  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30764  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30765  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30766  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30767  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30768  * @cfg {Function} renderer 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>
30769  * @cfg {Function} rendererTip 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>
30770  * 
30771  * @constructor
30772  * @param {String/HTMLElement/Element} el The container element
30773  * @param {Object} config
30774  */
30775 Roo.tree.TreePanel = function(el, config){
30776     var root = false;
30777     var loader = false;
30778     if (config.root) {
30779         root = config.root;
30780         delete config.root;
30781     }
30782     if (config.loader) {
30783         loader = config.loader;
30784         delete config.loader;
30785     }
30786     
30787     Roo.apply(this, config);
30788     Roo.tree.TreePanel.superclass.constructor.call(this);
30789     this.el = Roo.get(el);
30790     this.el.addClass('x-tree');
30791     //console.log(root);
30792     if (root) {
30793         this.setRootNode( Roo.factory(root, Roo.tree));
30794     }
30795     if (loader) {
30796         this.loader = Roo.factory(loader, Roo.tree);
30797     }
30798    /**
30799     * Read-only. The id of the container element becomes this TreePanel's id.
30800     */
30801    this.id = this.el.id;
30802    this.addEvents({
30803         /**
30804         * @event beforeload
30805         * Fires before a node is loaded, return false to cancel
30806         * @param {Node} node The node being loaded
30807         */
30808         "beforeload" : true,
30809         /**
30810         * @event load
30811         * Fires when a node is loaded
30812         * @param {Node} node The node that was loaded
30813         */
30814         "load" : true,
30815         /**
30816         * @event textchange
30817         * Fires when the text for a node is changed
30818         * @param {Node} node The node
30819         * @param {String} text The new text
30820         * @param {String} oldText The old text
30821         */
30822         "textchange" : true,
30823         /**
30824         * @event beforeexpand
30825         * Fires before a node is expanded, return false to cancel.
30826         * @param {Node} node The node
30827         * @param {Boolean} deep
30828         * @param {Boolean} anim
30829         */
30830         "beforeexpand" : true,
30831         /**
30832         * @event beforecollapse
30833         * Fires before a node is collapsed, return false to cancel.
30834         * @param {Node} node The node
30835         * @param {Boolean} deep
30836         * @param {Boolean} anim
30837         */
30838         "beforecollapse" : true,
30839         /**
30840         * @event expand
30841         * Fires when a node is expanded
30842         * @param {Node} node The node
30843         */
30844         "expand" : true,
30845         /**
30846         * @event disabledchange
30847         * Fires when the disabled status of a node changes
30848         * @param {Node} node The node
30849         * @param {Boolean} disabled
30850         */
30851         "disabledchange" : true,
30852         /**
30853         * @event collapse
30854         * Fires when a node is collapsed
30855         * @param {Node} node The node
30856         */
30857         "collapse" : true,
30858         /**
30859         * @event beforeclick
30860         * Fires before click processing on a node. Return false to cancel the default action.
30861         * @param {Node} node The node
30862         * @param {Roo.EventObject} e The event object
30863         */
30864         "beforeclick":true,
30865         /**
30866         * @event checkchange
30867         * Fires when a node with a checkbox's checked property changes
30868         * @param {Node} this This node
30869         * @param {Boolean} checked
30870         */
30871         "checkchange":true,
30872         /**
30873         * @event click
30874         * Fires when a node is clicked
30875         * @param {Node} node The node
30876         * @param {Roo.EventObject} e The event object
30877         */
30878         "click":true,
30879         /**
30880         * @event dblclick
30881         * Fires when a node is double clicked
30882         * @param {Node} node The node
30883         * @param {Roo.EventObject} e The event object
30884         */
30885         "dblclick":true,
30886         /**
30887         * @event contextmenu
30888         * Fires when a node is right clicked
30889         * @param {Node} node The node
30890         * @param {Roo.EventObject} e The event object
30891         */
30892         "contextmenu":true,
30893         /**
30894         * @event beforechildrenrendered
30895         * Fires right before the child nodes for a node are rendered
30896         * @param {Node} node The node
30897         */
30898         "beforechildrenrendered":true,
30899        /**
30900              * @event startdrag
30901              * Fires when a node starts being dragged
30902              * @param {Roo.tree.TreePanel} this
30903              * @param {Roo.tree.TreeNode} node
30904              * @param {event} e The raw browser event
30905              */ 
30906             "startdrag" : true,
30907             /**
30908              * @event enddrag
30909              * Fires when a drag operation is complete
30910              * @param {Roo.tree.TreePanel} this
30911              * @param {Roo.tree.TreeNode} node
30912              * @param {event} e The raw browser event
30913              */
30914             "enddrag" : true,
30915             /**
30916              * @event dragdrop
30917              * Fires when a dragged node is dropped on a valid DD target
30918              * @param {Roo.tree.TreePanel} this
30919              * @param {Roo.tree.TreeNode} node
30920              * @param {DD} dd The dd it was dropped on
30921              * @param {event} e The raw browser event
30922              */
30923             "dragdrop" : true,
30924             /**
30925              * @event beforenodedrop
30926              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
30927              * passed to handlers has the following properties:<br />
30928              * <ul style="padding:5px;padding-left:16px;">
30929              * <li>tree - The TreePanel</li>
30930              * <li>target - The node being targeted for the drop</li>
30931              * <li>data - The drag data from the drag source</li>
30932              * <li>point - The point of the drop - append, above or below</li>
30933              * <li>source - The drag source</li>
30934              * <li>rawEvent - Raw mouse event</li>
30935              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
30936              * to be inserted by setting them on this object.</li>
30937              * <li>cancel - Set this to true to cancel the drop.</li>
30938              * </ul>
30939              * @param {Object} dropEvent
30940              */
30941             "beforenodedrop" : true,
30942             /**
30943              * @event nodedrop
30944              * Fires after a DD object is dropped on a node in this tree. The dropEvent
30945              * passed to handlers has the following properties:<br />
30946              * <ul style="padding:5px;padding-left:16px;">
30947              * <li>tree - The TreePanel</li>
30948              * <li>target - The node being targeted for the drop</li>
30949              * <li>data - The drag data from the drag source</li>
30950              * <li>point - The point of the drop - append, above or below</li>
30951              * <li>source - The drag source</li>
30952              * <li>rawEvent - Raw mouse event</li>
30953              * <li>dropNode - Dropped node(s).</li>
30954              * </ul>
30955              * @param {Object} dropEvent
30956              */
30957             "nodedrop" : true,
30958              /**
30959              * @event nodedragover
30960              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
30961              * passed to handlers has the following properties:<br />
30962              * <ul style="padding:5px;padding-left:16px;">
30963              * <li>tree - The TreePanel</li>
30964              * <li>target - The node being targeted for the drop</li>
30965              * <li>data - The drag data from the drag source</li>
30966              * <li>point - The point of the drop - append, above or below</li>
30967              * <li>source - The drag source</li>
30968              * <li>rawEvent - Raw mouse event</li>
30969              * <li>dropNode - Drop node(s) provided by the source.</li>
30970              * <li>cancel - Set this to true to signal drop not allowed.</li>
30971              * </ul>
30972              * @param {Object} dragOverEvent
30973              */
30974             "nodedragover" : true
30975         
30976    });
30977    if(this.singleExpand){
30978        this.on("beforeexpand", this.restrictExpand, this);
30979    }
30980 };
30981 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
30982     rootVisible : true,
30983     animate: Roo.enableFx,
30984     lines : true,
30985     enableDD : false,
30986     hlDrop : Roo.enableFx,
30987   
30988     renderer: false,
30989     
30990     rendererTip: false,
30991     // private
30992     restrictExpand : function(node){
30993         var p = node.parentNode;
30994         if(p){
30995             if(p.expandedChild && p.expandedChild.parentNode == p){
30996                 p.expandedChild.collapse();
30997             }
30998             p.expandedChild = node;
30999         }
31000     },
31001
31002     // private override
31003     setRootNode : function(node){
31004         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31005         if(!this.rootVisible){
31006             node.ui = new Roo.tree.RootTreeNodeUI(node);
31007         }
31008         return node;
31009     },
31010
31011     /**
31012      * Returns the container element for this TreePanel
31013      */
31014     getEl : function(){
31015         return this.el;
31016     },
31017
31018     /**
31019      * Returns the default TreeLoader for this TreePanel
31020      */
31021     getLoader : function(){
31022         return this.loader;
31023     },
31024
31025     /**
31026      * Expand all nodes
31027      */
31028     expandAll : function(){
31029         this.root.expand(true);
31030     },
31031
31032     /**
31033      * Collapse all nodes
31034      */
31035     collapseAll : function(){
31036         this.root.collapse(true);
31037     },
31038
31039     /**
31040      * Returns the selection model used by this TreePanel
31041      */
31042     getSelectionModel : function(){
31043         if(!this.selModel){
31044             this.selModel = new Roo.tree.DefaultSelectionModel();
31045         }
31046         return this.selModel;
31047     },
31048
31049     /**
31050      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31051      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31052      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31053      * @return {Array}
31054      */
31055     getChecked : function(a, startNode){
31056         startNode = startNode || this.root;
31057         var r = [];
31058         var f = function(){
31059             if(this.attributes.checked){
31060                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31061             }
31062         }
31063         startNode.cascade(f);
31064         return r;
31065     },
31066
31067     /**
31068      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31069      * @param {String} path
31070      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31071      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31072      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31073      */
31074     expandPath : function(path, attr, callback){
31075         attr = attr || "id";
31076         var keys = path.split(this.pathSeparator);
31077         var curNode = this.root;
31078         if(curNode.attributes[attr] != keys[1]){ // invalid root
31079             if(callback){
31080                 callback(false, null);
31081             }
31082             return;
31083         }
31084         var index = 1;
31085         var f = function(){
31086             if(++index == keys.length){
31087                 if(callback){
31088                     callback(true, curNode);
31089                 }
31090                 return;
31091             }
31092             var c = curNode.findChild(attr, keys[index]);
31093             if(!c){
31094                 if(callback){
31095                     callback(false, curNode);
31096                 }
31097                 return;
31098             }
31099             curNode = c;
31100             c.expand(false, false, f);
31101         };
31102         curNode.expand(false, false, f);
31103     },
31104
31105     /**
31106      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31107      * @param {String} path
31108      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31109      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31110      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31111      */
31112     selectPath : function(path, attr, callback){
31113         attr = attr || "id";
31114         var keys = path.split(this.pathSeparator);
31115         var v = keys.pop();
31116         if(keys.length > 0){
31117             var f = function(success, node){
31118                 if(success && node){
31119                     var n = node.findChild(attr, v);
31120                     if(n){
31121                         n.select();
31122                         if(callback){
31123                             callback(true, n);
31124                         }
31125                     }else if(callback){
31126                         callback(false, n);
31127                     }
31128                 }else{
31129                     if(callback){
31130                         callback(false, n);
31131                     }
31132                 }
31133             };
31134             this.expandPath(keys.join(this.pathSeparator), attr, f);
31135         }else{
31136             this.root.select();
31137             if(callback){
31138                 callback(true, this.root);
31139             }
31140         }
31141     },
31142
31143     getTreeEl : function(){
31144         return this.el;
31145     },
31146
31147     /**
31148      * Trigger rendering of this TreePanel
31149      */
31150     render : function(){
31151         if (this.innerCt) {
31152             return this; // stop it rendering more than once!!
31153         }
31154         
31155         this.innerCt = this.el.createChild({tag:"ul",
31156                cls:"x-tree-root-ct " +
31157                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31158
31159         if(this.containerScroll){
31160             Roo.dd.ScrollManager.register(this.el);
31161         }
31162         if((this.enableDD || this.enableDrop) && !this.dropZone){
31163            /**
31164             * The dropZone used by this tree if drop is enabled
31165             * @type Roo.tree.TreeDropZone
31166             */
31167              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31168                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31169            });
31170         }
31171         if((this.enableDD || this.enableDrag) && !this.dragZone){
31172            /**
31173             * The dragZone used by this tree if drag is enabled
31174             * @type Roo.tree.TreeDragZone
31175             */
31176             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31177                ddGroup: this.ddGroup || "TreeDD",
31178                scroll: this.ddScroll
31179            });
31180         }
31181         this.getSelectionModel().init(this);
31182         if (!this.root) {
31183             console.log("ROOT not set in tree");
31184             return;
31185         }
31186         this.root.render();
31187         if(!this.rootVisible){
31188             this.root.renderChildren();
31189         }
31190         return this;
31191     }
31192 });/*
31193  * Based on:
31194  * Ext JS Library 1.1.1
31195  * Copyright(c) 2006-2007, Ext JS, LLC.
31196  *
31197  * Originally Released Under LGPL - original licence link has changed is not relivant.
31198  *
31199  * Fork - LGPL
31200  * <script type="text/javascript">
31201  */
31202  
31203
31204 /**
31205  * @class Roo.tree.DefaultSelectionModel
31206  * @extends Roo.util.Observable
31207  * The default single selection for a TreePanel.
31208  */
31209 Roo.tree.DefaultSelectionModel = function(){
31210    this.selNode = null;
31211    
31212    this.addEvents({
31213        /**
31214         * @event selectionchange
31215         * Fires when the selected node changes
31216         * @param {DefaultSelectionModel} this
31217         * @param {TreeNode} node the new selection
31218         */
31219        "selectionchange" : true,
31220
31221        /**
31222         * @event beforeselect
31223         * Fires before the selected node changes, return false to cancel the change
31224         * @param {DefaultSelectionModel} this
31225         * @param {TreeNode} node the new selection
31226         * @param {TreeNode} node the old selection
31227         */
31228        "beforeselect" : true
31229    });
31230 };
31231
31232 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31233     init : function(tree){
31234         this.tree = tree;
31235         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31236         tree.on("click", this.onNodeClick, this);
31237     },
31238     
31239     onNodeClick : function(node, e){
31240         if (e.ctrlKey && this.selNode == node)  {
31241             this.unselect(node);
31242             return;
31243         }
31244         this.select(node);
31245     },
31246     
31247     /**
31248      * Select a node.
31249      * @param {TreeNode} node The node to select
31250      * @return {TreeNode} The selected node
31251      */
31252     select : function(node){
31253         var last = this.selNode;
31254         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31255             if(last){
31256                 last.ui.onSelectedChange(false);
31257             }
31258             this.selNode = node;
31259             node.ui.onSelectedChange(true);
31260             this.fireEvent("selectionchange", this, node, last);
31261         }
31262         return node;
31263     },
31264     
31265     /**
31266      * Deselect a node.
31267      * @param {TreeNode} node The node to unselect
31268      */
31269     unselect : function(node){
31270         if(this.selNode == node){
31271             this.clearSelections();
31272         }    
31273     },
31274     
31275     /**
31276      * Clear all selections
31277      */
31278     clearSelections : function(){
31279         var n = this.selNode;
31280         if(n){
31281             n.ui.onSelectedChange(false);
31282             this.selNode = null;
31283             this.fireEvent("selectionchange", this, null);
31284         }
31285         return n;
31286     },
31287     
31288     /**
31289      * Get the selected node
31290      * @return {TreeNode} The selected node
31291      */
31292     getSelectedNode : function(){
31293         return this.selNode;    
31294     },
31295     
31296     /**
31297      * Returns true if the node is selected
31298      * @param {TreeNode} node The node to check
31299      * @return {Boolean}
31300      */
31301     isSelected : function(node){
31302         return this.selNode == node;  
31303     },
31304
31305     /**
31306      * Selects the node above the selected node in the tree, intelligently walking the nodes
31307      * @return TreeNode The new selection
31308      */
31309     selectPrevious : function(){
31310         var s = this.selNode || this.lastSelNode;
31311         if(!s){
31312             return null;
31313         }
31314         var ps = s.previousSibling;
31315         if(ps){
31316             if(!ps.isExpanded() || ps.childNodes.length < 1){
31317                 return this.select(ps);
31318             } else{
31319                 var lc = ps.lastChild;
31320                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31321                     lc = lc.lastChild;
31322                 }
31323                 return this.select(lc);
31324             }
31325         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31326             return this.select(s.parentNode);
31327         }
31328         return null;
31329     },
31330
31331     /**
31332      * Selects the node above the selected node in the tree, intelligently walking the nodes
31333      * @return TreeNode The new selection
31334      */
31335     selectNext : function(){
31336         var s = this.selNode || this.lastSelNode;
31337         if(!s){
31338             return null;
31339         }
31340         if(s.firstChild && s.isExpanded()){
31341              return this.select(s.firstChild);
31342          }else if(s.nextSibling){
31343              return this.select(s.nextSibling);
31344          }else if(s.parentNode){
31345             var newS = null;
31346             s.parentNode.bubble(function(){
31347                 if(this.nextSibling){
31348                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31349                     return false;
31350                 }
31351             });
31352             return newS;
31353          }
31354         return null;
31355     },
31356
31357     onKeyDown : function(e){
31358         var s = this.selNode || this.lastSelNode;
31359         // undesirable, but required
31360         var sm = this;
31361         if(!s){
31362             return;
31363         }
31364         var k = e.getKey();
31365         switch(k){
31366              case e.DOWN:
31367                  e.stopEvent();
31368                  this.selectNext();
31369              break;
31370              case e.UP:
31371                  e.stopEvent();
31372                  this.selectPrevious();
31373              break;
31374              case e.RIGHT:
31375                  e.preventDefault();
31376                  if(s.hasChildNodes()){
31377                      if(!s.isExpanded()){
31378                          s.expand();
31379                      }else if(s.firstChild){
31380                          this.select(s.firstChild, e);
31381                      }
31382                  }
31383              break;
31384              case e.LEFT:
31385                  e.preventDefault();
31386                  if(s.hasChildNodes() && s.isExpanded()){
31387                      s.collapse();
31388                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31389                      this.select(s.parentNode, e);
31390                  }
31391              break;
31392         };
31393     }
31394 });
31395
31396 /**
31397  * @class Roo.tree.MultiSelectionModel
31398  * @extends Roo.util.Observable
31399  * Multi selection for a TreePanel.
31400  */
31401 Roo.tree.MultiSelectionModel = function(){
31402    this.selNodes = [];
31403    this.selMap = {};
31404    this.addEvents({
31405        /**
31406         * @event selectionchange
31407         * Fires when the selected nodes change
31408         * @param {MultiSelectionModel} this
31409         * @param {Array} nodes Array of the selected nodes
31410         */
31411        "selectionchange" : true
31412    });
31413 };
31414
31415 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31416     init : function(tree){
31417         this.tree = tree;
31418         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31419         tree.on("click", this.onNodeClick, this);
31420     },
31421     
31422     onNodeClick : function(node, e){
31423         this.select(node, e, e.ctrlKey);
31424     },
31425     
31426     /**
31427      * Select a node.
31428      * @param {TreeNode} node The node to select
31429      * @param {EventObject} e (optional) An event associated with the selection
31430      * @param {Boolean} keepExisting True to retain existing selections
31431      * @return {TreeNode} The selected node
31432      */
31433     select : function(node, e, keepExisting){
31434         if(keepExisting !== true){
31435             this.clearSelections(true);
31436         }
31437         if(this.isSelected(node)){
31438             this.lastSelNode = node;
31439             return node;
31440         }
31441         this.selNodes.push(node);
31442         this.selMap[node.id] = node;
31443         this.lastSelNode = node;
31444         node.ui.onSelectedChange(true);
31445         this.fireEvent("selectionchange", this, this.selNodes);
31446         return node;
31447     },
31448     
31449     /**
31450      * Deselect a node.
31451      * @param {TreeNode} node The node to unselect
31452      */
31453     unselect : function(node){
31454         if(this.selMap[node.id]){
31455             node.ui.onSelectedChange(false);
31456             var sn = this.selNodes;
31457             var index = -1;
31458             if(sn.indexOf){
31459                 index = sn.indexOf(node);
31460             }else{
31461                 for(var i = 0, len = sn.length; i < len; i++){
31462                     if(sn[i] == node){
31463                         index = i;
31464                         break;
31465                     }
31466                 }
31467             }
31468             if(index != -1){
31469                 this.selNodes.splice(index, 1);
31470             }
31471             delete this.selMap[node.id];
31472             this.fireEvent("selectionchange", this, this.selNodes);
31473         }
31474     },
31475     
31476     /**
31477      * Clear all selections
31478      */
31479     clearSelections : function(suppressEvent){
31480         var sn = this.selNodes;
31481         if(sn.length > 0){
31482             for(var i = 0, len = sn.length; i < len; i++){
31483                 sn[i].ui.onSelectedChange(false);
31484             }
31485             this.selNodes = [];
31486             this.selMap = {};
31487             if(suppressEvent !== true){
31488                 this.fireEvent("selectionchange", this, this.selNodes);
31489             }
31490         }
31491     },
31492     
31493     /**
31494      * Returns true if the node is selected
31495      * @param {TreeNode} node The node to check
31496      * @return {Boolean}
31497      */
31498     isSelected : function(node){
31499         return this.selMap[node.id] ? true : false;  
31500     },
31501     
31502     /**
31503      * Returns an array of the selected nodes
31504      * @return {Array}
31505      */
31506     getSelectedNodes : function(){
31507         return this.selNodes;    
31508     },
31509
31510     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31511
31512     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31513
31514     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31515 });/*
31516  * Based on:
31517  * Ext JS Library 1.1.1
31518  * Copyright(c) 2006-2007, Ext JS, LLC.
31519  *
31520  * Originally Released Under LGPL - original licence link has changed is not relivant.
31521  *
31522  * Fork - LGPL
31523  * <script type="text/javascript">
31524  */
31525  
31526 /**
31527  * @class Roo.tree.TreeNode
31528  * @extends Roo.data.Node
31529  * @cfg {String} text The text for this node
31530  * @cfg {Boolean} expanded true to start the node expanded
31531  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31532  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31533  * @cfg {Boolean} disabled true to start the node disabled
31534  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31535  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31536  * @cfg {String} cls A css class to be added to the node
31537  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31538  * @cfg {String} href URL of the link used for the node (defaults to #)
31539  * @cfg {String} hrefTarget target frame for the link
31540  * @cfg {String} qtip An Ext QuickTip for the node
31541  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31542  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31543  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31544  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31545  * (defaults to undefined with no checkbox rendered)
31546  * @constructor
31547  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31548  */
31549 Roo.tree.TreeNode = function(attributes){
31550     attributes = attributes || {};
31551     if(typeof attributes == "string"){
31552         attributes = {text: attributes};
31553     }
31554     this.childrenRendered = false;
31555     this.rendered = false;
31556     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31557     this.expanded = attributes.expanded === true;
31558     this.isTarget = attributes.isTarget !== false;
31559     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31560     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31561
31562     /**
31563      * Read-only. The text for this node. To change it use setText().
31564      * @type String
31565      */
31566     this.text = attributes.text;
31567     /**
31568      * True if this node is disabled.
31569      * @type Boolean
31570      */
31571     this.disabled = attributes.disabled === true;
31572
31573     this.addEvents({
31574         /**
31575         * @event textchange
31576         * Fires when the text for this node is changed
31577         * @param {Node} this This node
31578         * @param {String} text The new text
31579         * @param {String} oldText The old text
31580         */
31581         "textchange" : true,
31582         /**
31583         * @event beforeexpand
31584         * Fires before this node is expanded, return false to cancel.
31585         * @param {Node} this This node
31586         * @param {Boolean} deep
31587         * @param {Boolean} anim
31588         */
31589         "beforeexpand" : true,
31590         /**
31591         * @event beforecollapse
31592         * Fires before this node is collapsed, return false to cancel.
31593         * @param {Node} this This node
31594         * @param {Boolean} deep
31595         * @param {Boolean} anim
31596         */
31597         "beforecollapse" : true,
31598         /**
31599         * @event expand
31600         * Fires when this node is expanded
31601         * @param {Node} this This node
31602         */
31603         "expand" : true,
31604         /**
31605         * @event disabledchange
31606         * Fires when the disabled status of this node changes
31607         * @param {Node} this This node
31608         * @param {Boolean} disabled
31609         */
31610         "disabledchange" : true,
31611         /**
31612         * @event collapse
31613         * Fires when this node is collapsed
31614         * @param {Node} this This node
31615         */
31616         "collapse" : true,
31617         /**
31618         * @event beforeclick
31619         * Fires before click processing. Return false to cancel the default action.
31620         * @param {Node} this This node
31621         * @param {Roo.EventObject} e The event object
31622         */
31623         "beforeclick":true,
31624         /**
31625         * @event checkchange
31626         * Fires when a node with a checkbox's checked property changes
31627         * @param {Node} this This node
31628         * @param {Boolean} checked
31629         */
31630         "checkchange":true,
31631         /**
31632         * @event click
31633         * Fires when this node is clicked
31634         * @param {Node} this This node
31635         * @param {Roo.EventObject} e The event object
31636         */
31637         "click":true,
31638         /**
31639         * @event dblclick
31640         * Fires when this node is double clicked
31641         * @param {Node} this This node
31642         * @param {Roo.EventObject} e The event object
31643         */
31644         "dblclick":true,
31645         /**
31646         * @event contextmenu
31647         * Fires when this node is right clicked
31648         * @param {Node} this This node
31649         * @param {Roo.EventObject} e The event object
31650         */
31651         "contextmenu":true,
31652         /**
31653         * @event beforechildrenrendered
31654         * Fires right before the child nodes for this node are rendered
31655         * @param {Node} this This node
31656         */
31657         "beforechildrenrendered":true
31658     });
31659
31660     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31661
31662     /**
31663      * Read-only. The UI for this node
31664      * @type TreeNodeUI
31665      */
31666     this.ui = new uiClass(this);
31667 };
31668 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31669     preventHScroll: true,
31670     /**
31671      * Returns true if this node is expanded
31672      * @return {Boolean}
31673      */
31674     isExpanded : function(){
31675         return this.expanded;
31676     },
31677
31678     /**
31679      * Returns the UI object for this node
31680      * @return {TreeNodeUI}
31681      */
31682     getUI : function(){
31683         return this.ui;
31684     },
31685
31686     // private override
31687     setFirstChild : function(node){
31688         var of = this.firstChild;
31689         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31690         if(this.childrenRendered && of && node != of){
31691             of.renderIndent(true, true);
31692         }
31693         if(this.rendered){
31694             this.renderIndent(true, true);
31695         }
31696     },
31697
31698     // private override
31699     setLastChild : function(node){
31700         var ol = this.lastChild;
31701         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31702         if(this.childrenRendered && ol && node != ol){
31703             ol.renderIndent(true, true);
31704         }
31705         if(this.rendered){
31706             this.renderIndent(true, true);
31707         }
31708     },
31709
31710     // these methods are overridden to provide lazy rendering support
31711     // private override
31712     appendChild : function(){
31713         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31714         if(node && this.childrenRendered){
31715             node.render();
31716         }
31717         this.ui.updateExpandIcon();
31718         return node;
31719     },
31720
31721     // private override
31722     removeChild : function(node){
31723         this.ownerTree.getSelectionModel().unselect(node);
31724         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31725         // if it's been rendered remove dom node
31726         if(this.childrenRendered){
31727             node.ui.remove();
31728         }
31729         if(this.childNodes.length < 1){
31730             this.collapse(false, false);
31731         }else{
31732             this.ui.updateExpandIcon();
31733         }
31734         if(!this.firstChild) {
31735             this.childrenRendered = false;
31736         }
31737         return node;
31738     },
31739
31740     // private override
31741     insertBefore : function(node, refNode){
31742         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31743         if(newNode && refNode && this.childrenRendered){
31744             node.render();
31745         }
31746         this.ui.updateExpandIcon();
31747         return newNode;
31748     },
31749
31750     /**
31751      * Sets the text for this node
31752      * @param {String} text
31753      */
31754     setText : function(text){
31755         var oldText = this.text;
31756         this.text = text;
31757         this.attributes.text = text;
31758         if(this.rendered){ // event without subscribing
31759             this.ui.onTextChange(this, text, oldText);
31760         }
31761         this.fireEvent("textchange", this, text, oldText);
31762     },
31763
31764     /**
31765      * Triggers selection of this node
31766      */
31767     select : function(){
31768         this.getOwnerTree().getSelectionModel().select(this);
31769     },
31770
31771     /**
31772      * Triggers deselection of this node
31773      */
31774     unselect : function(){
31775         this.getOwnerTree().getSelectionModel().unselect(this);
31776     },
31777
31778     /**
31779      * Returns true if this node is selected
31780      * @return {Boolean}
31781      */
31782     isSelected : function(){
31783         return this.getOwnerTree().getSelectionModel().isSelected(this);
31784     },
31785
31786     /**
31787      * Expand this node.
31788      * @param {Boolean} deep (optional) True to expand all children as well
31789      * @param {Boolean} anim (optional) false to cancel the default animation
31790      * @param {Function} callback (optional) A callback to be called when
31791      * expanding this node completes (does not wait for deep expand to complete).
31792      * Called with 1 parameter, this node.
31793      */
31794     expand : function(deep, anim, callback){
31795         if(!this.expanded){
31796             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31797                 return;
31798             }
31799             if(!this.childrenRendered){
31800                 this.renderChildren();
31801             }
31802             this.expanded = true;
31803             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31804                 this.ui.animExpand(function(){
31805                     this.fireEvent("expand", this);
31806                     if(typeof callback == "function"){
31807                         callback(this);
31808                     }
31809                     if(deep === true){
31810                         this.expandChildNodes(true);
31811                     }
31812                 }.createDelegate(this));
31813                 return;
31814             }else{
31815                 this.ui.expand();
31816                 this.fireEvent("expand", this);
31817                 if(typeof callback == "function"){
31818                     callback(this);
31819                 }
31820             }
31821         }else{
31822            if(typeof callback == "function"){
31823                callback(this);
31824            }
31825         }
31826         if(deep === true){
31827             this.expandChildNodes(true);
31828         }
31829     },
31830
31831     isHiddenRoot : function(){
31832         return this.isRoot && !this.getOwnerTree().rootVisible;
31833     },
31834
31835     /**
31836      * Collapse this node.
31837      * @param {Boolean} deep (optional) True to collapse all children as well
31838      * @param {Boolean} anim (optional) false to cancel the default animation
31839      */
31840     collapse : function(deep, anim){
31841         if(this.expanded && !this.isHiddenRoot()){
31842             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31843                 return;
31844             }
31845             this.expanded = false;
31846             if((this.getOwnerTree().animate && anim !== false) || anim){
31847                 this.ui.animCollapse(function(){
31848                     this.fireEvent("collapse", this);
31849                     if(deep === true){
31850                         this.collapseChildNodes(true);
31851                     }
31852                 }.createDelegate(this));
31853                 return;
31854             }else{
31855                 this.ui.collapse();
31856                 this.fireEvent("collapse", this);
31857             }
31858         }
31859         if(deep === true){
31860             var cs = this.childNodes;
31861             for(var i = 0, len = cs.length; i < len; i++) {
31862                 cs[i].collapse(true, false);
31863             }
31864         }
31865     },
31866
31867     // private
31868     delayedExpand : function(delay){
31869         if(!this.expandProcId){
31870             this.expandProcId = this.expand.defer(delay, this);
31871         }
31872     },
31873
31874     // private
31875     cancelExpand : function(){
31876         if(this.expandProcId){
31877             clearTimeout(this.expandProcId);
31878         }
31879         this.expandProcId = false;
31880     },
31881
31882     /**
31883      * Toggles expanded/collapsed state of the node
31884      */
31885     toggle : function(){
31886         if(this.expanded){
31887             this.collapse();
31888         }else{
31889             this.expand();
31890         }
31891     },
31892
31893     /**
31894      * Ensures all parent nodes are expanded
31895      */
31896     ensureVisible : function(callback){
31897         var tree = this.getOwnerTree();
31898         tree.expandPath(this.parentNode.getPath(), false, function(){
31899             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31900             Roo.callback(callback);
31901         }.createDelegate(this));
31902     },
31903
31904     /**
31905      * Expand all child nodes
31906      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31907      */
31908     expandChildNodes : function(deep){
31909         var cs = this.childNodes;
31910         for(var i = 0, len = cs.length; i < len; i++) {
31911                 cs[i].expand(deep);
31912         }
31913     },
31914
31915     /**
31916      * Collapse all child nodes
31917      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31918      */
31919     collapseChildNodes : function(deep){
31920         var cs = this.childNodes;
31921         for(var i = 0, len = cs.length; i < len; i++) {
31922                 cs[i].collapse(deep);
31923         }
31924     },
31925
31926     /**
31927      * Disables this node
31928      */
31929     disable : function(){
31930         this.disabled = true;
31931         this.unselect();
31932         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31933             this.ui.onDisableChange(this, true);
31934         }
31935         this.fireEvent("disabledchange", this, true);
31936     },
31937
31938     /**
31939      * Enables this node
31940      */
31941     enable : function(){
31942         this.disabled = false;
31943         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31944             this.ui.onDisableChange(this, false);
31945         }
31946         this.fireEvent("disabledchange", this, false);
31947     },
31948
31949     // private
31950     renderChildren : function(suppressEvent){
31951         if(suppressEvent !== false){
31952             this.fireEvent("beforechildrenrendered", this);
31953         }
31954         var cs = this.childNodes;
31955         for(var i = 0, len = cs.length; i < len; i++){
31956             cs[i].render(true);
31957         }
31958         this.childrenRendered = true;
31959     },
31960
31961     // private
31962     sort : function(fn, scope){
31963         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
31964         if(this.childrenRendered){
31965             var cs = this.childNodes;
31966             for(var i = 0, len = cs.length; i < len; i++){
31967                 cs[i].render(true);
31968             }
31969         }
31970     },
31971
31972     // private
31973     render : function(bulkRender){
31974         this.ui.render(bulkRender);
31975         if(!this.rendered){
31976             this.rendered = true;
31977             if(this.expanded){
31978                 this.expanded = false;
31979                 this.expand(false, false);
31980             }
31981         }
31982     },
31983
31984     // private
31985     renderIndent : function(deep, refresh){
31986         if(refresh){
31987             this.ui.childIndent = null;
31988         }
31989         this.ui.renderIndent();
31990         if(deep === true && this.childrenRendered){
31991             var cs = this.childNodes;
31992             for(var i = 0, len = cs.length; i < len; i++){
31993                 cs[i].renderIndent(true, refresh);
31994             }
31995         }
31996     }
31997 });/*
31998  * Based on:
31999  * Ext JS Library 1.1.1
32000  * Copyright(c) 2006-2007, Ext JS, LLC.
32001  *
32002  * Originally Released Under LGPL - original licence link has changed is not relivant.
32003  *
32004  * Fork - LGPL
32005  * <script type="text/javascript">
32006  */
32007  
32008 /**
32009  * @class Roo.tree.AsyncTreeNode
32010  * @extends Roo.tree.TreeNode
32011  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32012  * @constructor
32013  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32014  */
32015  Roo.tree.AsyncTreeNode = function(config){
32016     this.loaded = false;
32017     this.loading = false;
32018     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32019     /**
32020     * @event beforeload
32021     * Fires before this node is loaded, return false to cancel
32022     * @param {Node} this This node
32023     */
32024     this.addEvents({'beforeload':true, 'load': true});
32025     /**
32026     * @event load
32027     * Fires when this node is loaded
32028     * @param {Node} this This node
32029     */
32030     /**
32031      * The loader used by this node (defaults to using the tree's defined loader)
32032      * @type TreeLoader
32033      * @property loader
32034      */
32035 };
32036 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32037     expand : function(deep, anim, callback){
32038         if(this.loading){ // if an async load is already running, waiting til it's done
32039             var timer;
32040             var f = function(){
32041                 if(!this.loading){ // done loading
32042                     clearInterval(timer);
32043                     this.expand(deep, anim, callback);
32044                 }
32045             }.createDelegate(this);
32046             timer = setInterval(f, 200);
32047             return;
32048         }
32049         if(!this.loaded){
32050             if(this.fireEvent("beforeload", this) === false){
32051                 return;
32052             }
32053             this.loading = true;
32054             this.ui.beforeLoad(this);
32055             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32056             if(loader){
32057                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32058                 return;
32059             }
32060         }
32061         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32062     },
32063     
32064     /**
32065      * Returns true if this node is currently loading
32066      * @return {Boolean}
32067      */
32068     isLoading : function(){
32069         return this.loading;  
32070     },
32071     
32072     loadComplete : function(deep, anim, callback){
32073         this.loading = false;
32074         this.loaded = true;
32075         this.ui.afterLoad(this);
32076         this.fireEvent("load", this);
32077         this.expand(deep, anim, callback);
32078     },
32079     
32080     /**
32081      * Returns true if this node has been loaded
32082      * @return {Boolean}
32083      */
32084     isLoaded : function(){
32085         return this.loaded;
32086     },
32087     
32088     hasChildNodes : function(){
32089         if(!this.isLeaf() && !this.loaded){
32090             return true;
32091         }else{
32092             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32093         }
32094     },
32095
32096     /**
32097      * Trigger a reload for this node
32098      * @param {Function} callback
32099      */
32100     reload : function(callback){
32101         this.collapse(false, false);
32102         while(this.firstChild){
32103             this.removeChild(this.firstChild);
32104         }
32105         this.childrenRendered = false;
32106         this.loaded = false;
32107         if(this.isHiddenRoot()){
32108             this.expanded = false;
32109         }
32110         this.expand(false, false, callback);
32111     }
32112 });/*
32113  * Based on:
32114  * Ext JS Library 1.1.1
32115  * Copyright(c) 2006-2007, Ext JS, LLC.
32116  *
32117  * Originally Released Under LGPL - original licence link has changed is not relivant.
32118  *
32119  * Fork - LGPL
32120  * <script type="text/javascript">
32121  */
32122  
32123 /**
32124  * @class Roo.tree.TreeNodeUI
32125  * @constructor
32126  * @param {Object} node The node to render
32127  * The TreeNode UI implementation is separate from the
32128  * tree implementation. Unless you are customizing the tree UI,
32129  * you should never have to use this directly.
32130  */
32131 Roo.tree.TreeNodeUI = function(node){
32132     this.node = node;
32133     this.rendered = false;
32134     this.animating = false;
32135     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32136 };
32137
32138 Roo.tree.TreeNodeUI.prototype = {
32139     removeChild : function(node){
32140         if(this.rendered){
32141             this.ctNode.removeChild(node.ui.getEl());
32142         }
32143     },
32144
32145     beforeLoad : function(){
32146          this.addClass("x-tree-node-loading");
32147     },
32148
32149     afterLoad : function(){
32150          this.removeClass("x-tree-node-loading");
32151     },
32152
32153     onTextChange : function(node, text, oldText){
32154         if(this.rendered){
32155             this.textNode.innerHTML = text;
32156         }
32157     },
32158
32159     onDisableChange : function(node, state){
32160         this.disabled = state;
32161         if(state){
32162             this.addClass("x-tree-node-disabled");
32163         }else{
32164             this.removeClass("x-tree-node-disabled");
32165         }
32166     },
32167
32168     onSelectedChange : function(state){
32169         if(state){
32170             this.focus();
32171             this.addClass("x-tree-selected");
32172         }else{
32173             //this.blur();
32174             this.removeClass("x-tree-selected");
32175         }
32176     },
32177
32178     onMove : function(tree, node, oldParent, newParent, index, refNode){
32179         this.childIndent = null;
32180         if(this.rendered){
32181             var targetNode = newParent.ui.getContainer();
32182             if(!targetNode){//target not rendered
32183                 this.holder = document.createElement("div");
32184                 this.holder.appendChild(this.wrap);
32185                 return;
32186             }
32187             var insertBefore = refNode ? refNode.ui.getEl() : null;
32188             if(insertBefore){
32189                 targetNode.insertBefore(this.wrap, insertBefore);
32190             }else{
32191                 targetNode.appendChild(this.wrap);
32192             }
32193             this.node.renderIndent(true);
32194         }
32195     },
32196
32197     addClass : function(cls){
32198         if(this.elNode){
32199             Roo.fly(this.elNode).addClass(cls);
32200         }
32201     },
32202
32203     removeClass : function(cls){
32204         if(this.elNode){
32205             Roo.fly(this.elNode).removeClass(cls);
32206         }
32207     },
32208
32209     remove : function(){
32210         if(this.rendered){
32211             this.holder = document.createElement("div");
32212             this.holder.appendChild(this.wrap);
32213         }
32214     },
32215
32216     fireEvent : function(){
32217         return this.node.fireEvent.apply(this.node, arguments);
32218     },
32219
32220     initEvents : function(){
32221         this.node.on("move", this.onMove, this);
32222         var E = Roo.EventManager;
32223         var a = this.anchor;
32224
32225         var el = Roo.fly(a, '_treeui');
32226
32227         if(Roo.isOpera){ // opera render bug ignores the CSS
32228             el.setStyle("text-decoration", "none");
32229         }
32230
32231         el.on("click", this.onClick, this);
32232         el.on("dblclick", this.onDblClick, this);
32233
32234         if(this.checkbox){
32235             Roo.EventManager.on(this.checkbox,
32236                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32237         }
32238
32239         el.on("contextmenu", this.onContextMenu, this);
32240
32241         var icon = Roo.fly(this.iconNode);
32242         icon.on("click", this.onClick, this);
32243         icon.on("dblclick", this.onDblClick, this);
32244         icon.on("contextmenu", this.onContextMenu, this);
32245         E.on(this.ecNode, "click", this.ecClick, this, true);
32246
32247         if(this.node.disabled){
32248             this.addClass("x-tree-node-disabled");
32249         }
32250         if(this.node.hidden){
32251             this.addClass("x-tree-node-disabled");
32252         }
32253         var ot = this.node.getOwnerTree();
32254         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32255         if(dd && (!this.node.isRoot || ot.rootVisible)){
32256             Roo.dd.Registry.register(this.elNode, {
32257                 node: this.node,
32258                 handles: this.getDDHandles(),
32259                 isHandle: false
32260             });
32261         }
32262     },
32263
32264     getDDHandles : function(){
32265         return [this.iconNode, this.textNode];
32266     },
32267
32268     hide : function(){
32269         if(this.rendered){
32270             this.wrap.style.display = "none";
32271         }
32272     },
32273
32274     show : function(){
32275         if(this.rendered){
32276             this.wrap.style.display = "";
32277         }
32278     },
32279
32280     onContextMenu : function(e){
32281         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32282             e.preventDefault();
32283             this.focus();
32284             this.fireEvent("contextmenu", this.node, e);
32285         }
32286     },
32287
32288     onClick : function(e){
32289         if(this.dropping){
32290             e.stopEvent();
32291             return;
32292         }
32293         if(this.fireEvent("beforeclick", this.node, e) !== false){
32294             if(!this.disabled && this.node.attributes.href){
32295                 this.fireEvent("click", this.node, e);
32296                 return;
32297             }
32298             e.preventDefault();
32299             if(this.disabled){
32300                 return;
32301             }
32302
32303             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32304                 this.node.toggle();
32305             }
32306
32307             this.fireEvent("click", this.node, e);
32308         }else{
32309             e.stopEvent();
32310         }
32311     },
32312
32313     onDblClick : function(e){
32314         e.preventDefault();
32315         if(this.disabled){
32316             return;
32317         }
32318         if(this.checkbox){
32319             this.toggleCheck();
32320         }
32321         if(!this.animating && this.node.hasChildNodes()){
32322             this.node.toggle();
32323         }
32324         this.fireEvent("dblclick", this.node, e);
32325     },
32326
32327     onCheckChange : function(){
32328         var checked = this.checkbox.checked;
32329         this.node.attributes.checked = checked;
32330         this.fireEvent('checkchange', this.node, checked);
32331     },
32332
32333     ecClick : function(e){
32334         if(!this.animating && this.node.hasChildNodes()){
32335             this.node.toggle();
32336         }
32337     },
32338
32339     startDrop : function(){
32340         this.dropping = true;
32341     },
32342
32343     // delayed drop so the click event doesn't get fired on a drop
32344     endDrop : function(){
32345        setTimeout(function(){
32346            this.dropping = false;
32347        }.createDelegate(this), 50);
32348     },
32349
32350     expand : function(){
32351         this.updateExpandIcon();
32352         this.ctNode.style.display = "";
32353     },
32354
32355     focus : function(){
32356         if(!this.node.preventHScroll){
32357             try{this.anchor.focus();
32358             }catch(e){}
32359         }else if(!Roo.isIE){
32360             try{
32361                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32362                 var l = noscroll.scrollLeft;
32363                 this.anchor.focus();
32364                 noscroll.scrollLeft = l;
32365             }catch(e){}
32366         }
32367     },
32368
32369     toggleCheck : function(value){
32370         var cb = this.checkbox;
32371         if(cb){
32372             cb.checked = (value === undefined ? !cb.checked : value);
32373         }
32374     },
32375
32376     blur : function(){
32377         try{
32378             this.anchor.blur();
32379         }catch(e){}
32380     },
32381
32382     animExpand : function(callback){
32383         var ct = Roo.get(this.ctNode);
32384         ct.stopFx();
32385         if(!this.node.hasChildNodes()){
32386             this.updateExpandIcon();
32387             this.ctNode.style.display = "";
32388             Roo.callback(callback);
32389             return;
32390         }
32391         this.animating = true;
32392         this.updateExpandIcon();
32393
32394         ct.slideIn('t', {
32395            callback : function(){
32396                this.animating = false;
32397                Roo.callback(callback);
32398             },
32399             scope: this,
32400             duration: this.node.ownerTree.duration || .25
32401         });
32402     },
32403
32404     highlight : function(){
32405         var tree = this.node.getOwnerTree();
32406         Roo.fly(this.wrap).highlight(
32407             tree.hlColor || "C3DAF9",
32408             {endColor: tree.hlBaseColor}
32409         );
32410     },
32411
32412     collapse : function(){
32413         this.updateExpandIcon();
32414         this.ctNode.style.display = "none";
32415     },
32416
32417     animCollapse : function(callback){
32418         var ct = Roo.get(this.ctNode);
32419         ct.enableDisplayMode('block');
32420         ct.stopFx();
32421
32422         this.animating = true;
32423         this.updateExpandIcon();
32424
32425         ct.slideOut('t', {
32426             callback : function(){
32427                this.animating = false;
32428                Roo.callback(callback);
32429             },
32430             scope: this,
32431             duration: this.node.ownerTree.duration || .25
32432         });
32433     },
32434
32435     getContainer : function(){
32436         return this.ctNode;
32437     },
32438
32439     getEl : function(){
32440         return this.wrap;
32441     },
32442
32443     appendDDGhost : function(ghostNode){
32444         ghostNode.appendChild(this.elNode.cloneNode(true));
32445     },
32446
32447     getDDRepairXY : function(){
32448         return Roo.lib.Dom.getXY(this.iconNode);
32449     },
32450
32451     onRender : function(){
32452         this.render();
32453     },
32454
32455     render : function(bulkRender){
32456         var n = this.node, a = n.attributes;
32457         var targetNode = n.parentNode ?
32458               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32459
32460         if(!this.rendered){
32461             this.rendered = true;
32462
32463             this.renderElements(n, a, targetNode, bulkRender);
32464
32465             if(a.qtip){
32466                if(this.textNode.setAttributeNS){
32467                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32468                    if(a.qtipTitle){
32469                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32470                    }
32471                }else{
32472                    this.textNode.setAttribute("ext:qtip", a.qtip);
32473                    if(a.qtipTitle){
32474                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32475                    }
32476                }
32477             }else if(a.qtipCfg){
32478                 a.qtipCfg.target = Roo.id(this.textNode);
32479                 Roo.QuickTips.register(a.qtipCfg);
32480             }
32481             this.initEvents();
32482             if(!this.node.expanded){
32483                 this.updateExpandIcon();
32484             }
32485         }else{
32486             if(bulkRender === true) {
32487                 targetNode.appendChild(this.wrap);
32488             }
32489         }
32490     },
32491
32492     renderElements : function(n, a, targetNode, bulkRender){
32493         // add some indent caching, this helps performance when rendering a large tree
32494         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32495         var t = n.getOwnerTree();
32496         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32497         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32498         var cb = typeof a.checked == 'boolean';
32499         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32500         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32501             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32502             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32503             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32504             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32505             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32506              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32507                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32508             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32509             "</li>"];
32510
32511         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32512             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32513                                 n.nextSibling.ui.getEl(), buf.join(""));
32514         }else{
32515             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32516         }
32517
32518         this.elNode = this.wrap.childNodes[0];
32519         this.ctNode = this.wrap.childNodes[1];
32520         var cs = this.elNode.childNodes;
32521         this.indentNode = cs[0];
32522         this.ecNode = cs[1];
32523         this.iconNode = cs[2];
32524         var index = 3;
32525         if(cb){
32526             this.checkbox = cs[3];
32527             index++;
32528         }
32529         this.anchor = cs[index];
32530         this.textNode = cs[index].firstChild;
32531     },
32532
32533     getAnchor : function(){
32534         return this.anchor;
32535     },
32536
32537     getTextEl : function(){
32538         return this.textNode;
32539     },
32540
32541     getIconEl : function(){
32542         return this.iconNode;
32543     },
32544
32545     isChecked : function(){
32546         return this.checkbox ? this.checkbox.checked : false;
32547     },
32548
32549     updateExpandIcon : function(){
32550         if(this.rendered){
32551             var n = this.node, c1, c2;
32552             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32553             var hasChild = n.hasChildNodes();
32554             if(hasChild){
32555                 if(n.expanded){
32556                     cls += "-minus";
32557                     c1 = "x-tree-node-collapsed";
32558                     c2 = "x-tree-node-expanded";
32559                 }else{
32560                     cls += "-plus";
32561                     c1 = "x-tree-node-expanded";
32562                     c2 = "x-tree-node-collapsed";
32563                 }
32564                 if(this.wasLeaf){
32565                     this.removeClass("x-tree-node-leaf");
32566                     this.wasLeaf = false;
32567                 }
32568                 if(this.c1 != c1 || this.c2 != c2){
32569                     Roo.fly(this.elNode).replaceClass(c1, c2);
32570                     this.c1 = c1; this.c2 = c2;
32571                 }
32572             }else{
32573                 if(!this.wasLeaf){
32574                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32575                     delete this.c1;
32576                     delete this.c2;
32577                     this.wasLeaf = true;
32578                 }
32579             }
32580             var ecc = "x-tree-ec-icon "+cls;
32581             if(this.ecc != ecc){
32582                 this.ecNode.className = ecc;
32583                 this.ecc = ecc;
32584             }
32585         }
32586     },
32587
32588     getChildIndent : function(){
32589         if(!this.childIndent){
32590             var buf = [];
32591             var p = this.node;
32592             while(p){
32593                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32594                     if(!p.isLast()) {
32595                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32596                     } else {
32597                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32598                     }
32599                 }
32600                 p = p.parentNode;
32601             }
32602             this.childIndent = buf.join("");
32603         }
32604         return this.childIndent;
32605     },
32606
32607     renderIndent : function(){
32608         if(this.rendered){
32609             var indent = "";
32610             var p = this.node.parentNode;
32611             if(p){
32612                 indent = p.ui.getChildIndent();
32613             }
32614             if(this.indentMarkup != indent){ // don't rerender if not required
32615                 this.indentNode.innerHTML = indent;
32616                 this.indentMarkup = indent;
32617             }
32618             this.updateExpandIcon();
32619         }
32620     }
32621 };
32622
32623 Roo.tree.RootTreeNodeUI = function(){
32624     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32625 };
32626 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32627     render : function(){
32628         if(!this.rendered){
32629             var targetNode = this.node.ownerTree.innerCt.dom;
32630             this.node.expanded = true;
32631             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32632             this.wrap = this.ctNode = targetNode.firstChild;
32633         }
32634     },
32635     collapse : function(){
32636     },
32637     expand : function(){
32638     }
32639 });/*
32640  * Based on:
32641  * Ext JS Library 1.1.1
32642  * Copyright(c) 2006-2007, Ext JS, LLC.
32643  *
32644  * Originally Released Under LGPL - original licence link has changed is not relivant.
32645  *
32646  * Fork - LGPL
32647  * <script type="text/javascript">
32648  */
32649 /**
32650  * @class Roo.tree.TreeLoader
32651  * @extends Roo.util.Observable
32652  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32653  * nodes from a specified URL. The response must be a javascript Array definition
32654  * who's elements are node definition objects. eg:
32655  * <pre><code>
32656    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32657     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32658 </code></pre>
32659  * <br><br>
32660  * A server request is sent, and child nodes are loaded only when a node is expanded.
32661  * The loading node's id is passed to the server under the parameter name "node" to
32662  * enable the server to produce the correct child nodes.
32663  * <br><br>
32664  * To pass extra parameters, an event handler may be attached to the "beforeload"
32665  * event, and the parameters specified in the TreeLoader's baseParams property:
32666  * <pre><code>
32667     myTreeLoader.on("beforeload", function(treeLoader, node) {
32668         this.baseParams.category = node.attributes.category;
32669     }, this);
32670 </code></pre><
32671  * This would pass an HTTP parameter called "category" to the server containing
32672  * the value of the Node's "category" attribute.
32673  * @constructor
32674  * Creates a new Treeloader.
32675  * @param {Object} config A config object containing config properties.
32676  */
32677 Roo.tree.TreeLoader = function(config){
32678     this.baseParams = {};
32679     this.requestMethod = "POST";
32680     Roo.apply(this, config);
32681
32682     this.addEvents({
32683     
32684         /**
32685          * @event beforeload
32686          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32687          * @param {Object} This TreeLoader object.
32688          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32689          * @param {Object} callback The callback function specified in the {@link #load} call.
32690          */
32691         beforeload : true,
32692         /**
32693          * @event load
32694          * Fires when the node has been successfuly loaded.
32695          * @param {Object} This TreeLoader object.
32696          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32697          * @param {Object} response The response object containing the data from the server.
32698          */
32699         load : true,
32700         /**
32701          * @event loadexception
32702          * Fires if the network request failed.
32703          * @param {Object} This TreeLoader object.
32704          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32705          * @param {Object} response The response object containing the data from the server.
32706          */
32707         loadexception : true,
32708         /**
32709          * @event create
32710          * Fires before a node is created, enabling you to return custom Node types 
32711          * @param {Object} This TreeLoader object.
32712          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32713          */
32714         create : true
32715     });
32716
32717     Roo.tree.TreeLoader.superclass.constructor.call(this);
32718 };
32719
32720 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32721     /**
32722     * @cfg {String} dataUrl The URL from which to request a Json string which
32723     * specifies an array of node definition object representing the child nodes
32724     * to be loaded.
32725     */
32726     /**
32727     * @cfg {Object} baseParams (optional) An object containing properties which
32728     * specify HTTP parameters to be passed to each request for child nodes.
32729     */
32730     /**
32731     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32732     * created by this loader. If the attributes sent by the server have an attribute in this object,
32733     * they take priority.
32734     */
32735     /**
32736     * @cfg {Object} uiProviders (optional) An object containing properties which
32737     * 
32738     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32739     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32740     * <i>uiProvider</i> attribute of a returned child node is a string rather
32741     * than a reference to a TreeNodeUI implementation, this that string value
32742     * is used as a property name in the uiProviders object. You can define the provider named
32743     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32744     */
32745     uiProviders : {},
32746
32747     /**
32748     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32749     * child nodes before loading.
32750     */
32751     clearOnLoad : true,
32752
32753     /**
32754     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32755     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32756     * Grid query { data : [ .....] }
32757     */
32758     
32759     root : false,
32760      /**
32761     * @cfg {String} queryParam (optional) 
32762     * Name of the query as it will be passed on the querystring (defaults to 'node')
32763     * eg. the request will be ?node=[id]
32764     */
32765     
32766     
32767     queryParam: false,
32768     
32769     /**
32770      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32771      * This is called automatically when a node is expanded, but may be used to reload
32772      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32773      * @param {Roo.tree.TreeNode} node
32774      * @param {Function} callback
32775      */
32776     load : function(node, callback){
32777         if(this.clearOnLoad){
32778             while(node.firstChild){
32779                 node.removeChild(node.firstChild);
32780             }
32781         }
32782         if(node.attributes.children){ // preloaded json children
32783             var cs = node.attributes.children;
32784             for(var i = 0, len = cs.length; i < len; i++){
32785                 node.appendChild(this.createNode(cs[i]));
32786             }
32787             if(typeof callback == "function"){
32788                 callback();
32789             }
32790         }else if(this.dataUrl){
32791             this.requestData(node, callback);
32792         }
32793     },
32794
32795     getParams: function(node){
32796         var buf = [], bp = this.baseParams;
32797         for(var key in bp){
32798             if(typeof bp[key] != "function"){
32799                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32800             }
32801         }
32802         var n = this.queryParam === false ? 'node' : this.queryParam;
32803         buf.push(n + "=", encodeURIComponent(node.id));
32804         return buf.join("");
32805     },
32806
32807     requestData : function(node, callback){
32808         if(this.fireEvent("beforeload", this, node, callback) !== false){
32809             this.transId = Roo.Ajax.request({
32810                 method:this.requestMethod,
32811                 url: this.dataUrl||this.url,
32812                 success: this.handleResponse,
32813                 failure: this.handleFailure,
32814                 scope: this,
32815                 argument: {callback: callback, node: node},
32816                 params: this.getParams(node)
32817             });
32818         }else{
32819             // if the load is cancelled, make sure we notify
32820             // the node that we are done
32821             if(typeof callback == "function"){
32822                 callback();
32823             }
32824         }
32825     },
32826
32827     isLoading : function(){
32828         return this.transId ? true : false;
32829     },
32830
32831     abort : function(){
32832         if(this.isLoading()){
32833             Roo.Ajax.abort(this.transId);
32834         }
32835     },
32836
32837     // private
32838     createNode : function(attr){
32839         // apply baseAttrs, nice idea Corey!
32840         if(this.baseAttrs){
32841             Roo.applyIf(attr, this.baseAttrs);
32842         }
32843         if(this.applyLoader !== false){
32844             attr.loader = this;
32845         }
32846         // uiProvider = depreciated..
32847         
32848         if(typeof(attr.uiProvider) == 'string'){
32849            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32850                 /**  eval:var:attr */ eval(attr.uiProvider);
32851         }
32852         if(typeof(this.uiProviders['default']) != 'undefined') {
32853             attr.uiProvider = this.uiProviders['default'];
32854         }
32855         
32856         this.fireEvent('create', this, attr);
32857         
32858         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32859         return(attr.leaf ?
32860                         new Roo.tree.TreeNode(attr) :
32861                         new Roo.tree.AsyncTreeNode(attr));
32862     },
32863
32864     processResponse : function(response, node, callback){
32865         var json = response.responseText;
32866         try {
32867             
32868             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32869             if (this.root !== false) {
32870                 o = o[this.root];
32871             }
32872             
32873             for(var i = 0, len = o.length; i < len; i++){
32874                 var n = this.createNode(o[i]);
32875                 if(n){
32876                     node.appendChild(n);
32877                 }
32878             }
32879             if(typeof callback == "function"){
32880                 callback(this, node);
32881             }
32882         }catch(e){
32883             this.handleFailure(response);
32884         }
32885     },
32886
32887     handleResponse : function(response){
32888         this.transId = false;
32889         var a = response.argument;
32890         this.processResponse(response, a.node, a.callback);
32891         this.fireEvent("load", this, a.node, response);
32892     },
32893
32894     handleFailure : function(response){
32895         this.transId = false;
32896         var a = response.argument;
32897         this.fireEvent("loadexception", this, a.node, response);
32898         if(typeof a.callback == "function"){
32899             a.callback(this, a.node);
32900         }
32901     }
32902 });/*
32903  * Based on:
32904  * Ext JS Library 1.1.1
32905  * Copyright(c) 2006-2007, Ext JS, LLC.
32906  *
32907  * Originally Released Under LGPL - original licence link has changed is not relivant.
32908  *
32909  * Fork - LGPL
32910  * <script type="text/javascript">
32911  */
32912
32913 /**
32914 * @class Roo.tree.TreeFilter
32915 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32916 * @param {TreePanel} tree
32917 * @param {Object} config (optional)
32918  */
32919 Roo.tree.TreeFilter = function(tree, config){
32920     this.tree = tree;
32921     this.filtered = {};
32922     Roo.apply(this, config);
32923 };
32924
32925 Roo.tree.TreeFilter.prototype = {
32926     clearBlank:false,
32927     reverse:false,
32928     autoClear:false,
32929     remove:false,
32930
32931      /**
32932      * Filter the data by a specific attribute.
32933      * @param {String/RegExp} value Either string that the attribute value
32934      * should start with or a RegExp to test against the attribute
32935      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
32936      * @param {TreeNode} startNode (optional) The node to start the filter at.
32937      */
32938     filter : function(value, attr, startNode){
32939         attr = attr || "text";
32940         var f;
32941         if(typeof value == "string"){
32942             var vlen = value.length;
32943             // auto clear empty filter
32944             if(vlen == 0 && this.clearBlank){
32945                 this.clear();
32946                 return;
32947             }
32948             value = value.toLowerCase();
32949             f = function(n){
32950                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
32951             };
32952         }else if(value.exec){ // regex?
32953             f = function(n){
32954                 return value.test(n.attributes[attr]);
32955             };
32956         }else{
32957             throw 'Illegal filter type, must be string or regex';
32958         }
32959         this.filterBy(f, null, startNode);
32960         },
32961
32962     /**
32963      * Filter by a function. The passed function will be called with each
32964      * node in the tree (or from the startNode). If the function returns true, the node is kept
32965      * otherwise it is filtered. If a node is filtered, its children are also filtered.
32966      * @param {Function} fn The filter function
32967      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
32968      */
32969     filterBy : function(fn, scope, startNode){
32970         startNode = startNode || this.tree.root;
32971         if(this.autoClear){
32972             this.clear();
32973         }
32974         var af = this.filtered, rv = this.reverse;
32975         var f = function(n){
32976             if(n == startNode){
32977                 return true;
32978             }
32979             if(af[n.id]){
32980                 return false;
32981             }
32982             var m = fn.call(scope || n, n);
32983             if(!m || rv){
32984                 af[n.id] = n;
32985                 n.ui.hide();
32986                 return false;
32987             }
32988             return true;
32989         };
32990         startNode.cascade(f);
32991         if(this.remove){
32992            for(var id in af){
32993                if(typeof id != "function"){
32994                    var n = af[id];
32995                    if(n && n.parentNode){
32996                        n.parentNode.removeChild(n);
32997                    }
32998                }
32999            }
33000         }
33001     },
33002
33003     /**
33004      * Clears the current filter. Note: with the "remove" option
33005      * set a filter cannot be cleared.
33006      */
33007     clear : function(){
33008         var t = this.tree;
33009         var af = this.filtered;
33010         for(var id in af){
33011             if(typeof id != "function"){
33012                 var n = af[id];
33013                 if(n){
33014                     n.ui.show();
33015                 }
33016             }
33017         }
33018         this.filtered = {};
33019     }
33020 };
33021 /*
33022  * Based on:
33023  * Ext JS Library 1.1.1
33024  * Copyright(c) 2006-2007, Ext JS, LLC.
33025  *
33026  * Originally Released Under LGPL - original licence link has changed is not relivant.
33027  *
33028  * Fork - LGPL
33029  * <script type="text/javascript">
33030  */
33031  
33032
33033 /**
33034  * @class Roo.tree.TreeSorter
33035  * Provides sorting of nodes in a TreePanel
33036  * 
33037  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33038  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33039  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33040  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33041  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33042  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33043  * @constructor
33044  * @param {TreePanel} tree
33045  * @param {Object} config
33046  */
33047 Roo.tree.TreeSorter = function(tree, config){
33048     Roo.apply(this, config);
33049     tree.on("beforechildrenrendered", this.doSort, this);
33050     tree.on("append", this.updateSort, this);
33051     tree.on("insert", this.updateSort, this);
33052     
33053     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33054     var p = this.property || "text";
33055     var sortType = this.sortType;
33056     var fs = this.folderSort;
33057     var cs = this.caseSensitive === true;
33058     var leafAttr = this.leafAttr || 'leaf';
33059
33060     this.sortFn = function(n1, n2){
33061         if(fs){
33062             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33063                 return 1;
33064             }
33065             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33066                 return -1;
33067             }
33068         }
33069         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33070         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33071         if(v1 < v2){
33072                         return dsc ? +1 : -1;
33073                 }else if(v1 > v2){
33074                         return dsc ? -1 : +1;
33075         }else{
33076                 return 0;
33077         }
33078     };
33079 };
33080
33081 Roo.tree.TreeSorter.prototype = {
33082     doSort : function(node){
33083         node.sort(this.sortFn);
33084     },
33085     
33086     compareNodes : function(n1, n2){
33087         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33088     },
33089     
33090     updateSort : function(tree, node){
33091         if(node.childrenRendered){
33092             this.doSort.defer(1, this, [node]);
33093         }
33094     }
33095 };/*
33096  * Based on:
33097  * Ext JS Library 1.1.1
33098  * Copyright(c) 2006-2007, Ext JS, LLC.
33099  *
33100  * Originally Released Under LGPL - original licence link has changed is not relivant.
33101  *
33102  * Fork - LGPL
33103  * <script type="text/javascript">
33104  */
33105
33106 if(Roo.dd.DropZone){
33107     
33108 Roo.tree.TreeDropZone = function(tree, config){
33109     this.allowParentInsert = false;
33110     this.allowContainerDrop = false;
33111     this.appendOnly = false;
33112     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33113     this.tree = tree;
33114     this.lastInsertClass = "x-tree-no-status";
33115     this.dragOverData = {};
33116 };
33117
33118 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33119     ddGroup : "TreeDD",
33120     
33121     expandDelay : 1000,
33122     
33123     expandNode : function(node){
33124         if(node.hasChildNodes() && !node.isExpanded()){
33125             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33126         }
33127     },
33128     
33129     queueExpand : function(node){
33130         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33131     },
33132     
33133     cancelExpand : function(){
33134         if(this.expandProcId){
33135             clearTimeout(this.expandProcId);
33136             this.expandProcId = false;
33137         }
33138     },
33139     
33140     isValidDropPoint : function(n, pt, dd, e, data){
33141         if(!n || !data){ return false; }
33142         var targetNode = n.node;
33143         var dropNode = data.node;
33144         // default drop rules
33145         if(!(targetNode && targetNode.isTarget && pt)){
33146             return false;
33147         }
33148         if(pt == "append" && targetNode.allowChildren === false){
33149             return false;
33150         }
33151         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33152             return false;
33153         }
33154         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33155             return false;
33156         }
33157         // reuse the object
33158         var overEvent = this.dragOverData;
33159         overEvent.tree = this.tree;
33160         overEvent.target = targetNode;
33161         overEvent.data = data;
33162         overEvent.point = pt;
33163         overEvent.source = dd;
33164         overEvent.rawEvent = e;
33165         overEvent.dropNode = dropNode;
33166         overEvent.cancel = false;  
33167         var result = this.tree.fireEvent("nodedragover", overEvent);
33168         return overEvent.cancel === false && result !== false;
33169     },
33170     
33171     getDropPoint : function(e, n, dd){
33172         var tn = n.node;
33173         if(tn.isRoot){
33174             return tn.allowChildren !== false ? "append" : false; // always append for root
33175         }
33176         var dragEl = n.ddel;
33177         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33178         var y = Roo.lib.Event.getPageY(e);
33179         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33180         
33181         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33182         var noAppend = tn.allowChildren === false;
33183         if(this.appendOnly || tn.parentNode.allowChildren === false){
33184             return noAppend ? false : "append";
33185         }
33186         var noBelow = false;
33187         if(!this.allowParentInsert){
33188             noBelow = tn.hasChildNodes() && tn.isExpanded();
33189         }
33190         var q = (b - t) / (noAppend ? 2 : 3);
33191         if(y >= t && y < (t + q)){
33192             return "above";
33193         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33194             return "below";
33195         }else{
33196             return "append";
33197         }
33198     },
33199     
33200     onNodeEnter : function(n, dd, e, data){
33201         this.cancelExpand();
33202     },
33203     
33204     onNodeOver : function(n, dd, e, data){
33205         var pt = this.getDropPoint(e, n, dd);
33206         var node = n.node;
33207         
33208         // auto node expand check
33209         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33210             this.queueExpand(node);
33211         }else if(pt != "append"){
33212             this.cancelExpand();
33213         }
33214         
33215         // set the insert point style on the target node
33216         var returnCls = this.dropNotAllowed;
33217         if(this.isValidDropPoint(n, pt, dd, e, data)){
33218            if(pt){
33219                var el = n.ddel;
33220                var cls;
33221                if(pt == "above"){
33222                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33223                    cls = "x-tree-drag-insert-above";
33224                }else if(pt == "below"){
33225                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33226                    cls = "x-tree-drag-insert-below";
33227                }else{
33228                    returnCls = "x-tree-drop-ok-append";
33229                    cls = "x-tree-drag-append";
33230                }
33231                if(this.lastInsertClass != cls){
33232                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33233                    this.lastInsertClass = cls;
33234                }
33235            }
33236        }
33237        return returnCls;
33238     },
33239     
33240     onNodeOut : function(n, dd, e, data){
33241         this.cancelExpand();
33242         this.removeDropIndicators(n);
33243     },
33244     
33245     onNodeDrop : function(n, dd, e, data){
33246         var point = this.getDropPoint(e, n, dd);
33247         var targetNode = n.node;
33248         targetNode.ui.startDrop();
33249         if(!this.isValidDropPoint(n, point, dd, e, data)){
33250             targetNode.ui.endDrop();
33251             return false;
33252         }
33253         // first try to find the drop node
33254         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33255         var dropEvent = {
33256             tree : this.tree,
33257             target: targetNode,
33258             data: data,
33259             point: point,
33260             source: dd,
33261             rawEvent: e,
33262             dropNode: dropNode,
33263             cancel: !dropNode   
33264         };
33265         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33266         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33267             targetNode.ui.endDrop();
33268             return false;
33269         }
33270         // allow target changing
33271         targetNode = dropEvent.target;
33272         if(point == "append" && !targetNode.isExpanded()){
33273             targetNode.expand(false, null, function(){
33274                 this.completeDrop(dropEvent);
33275             }.createDelegate(this));
33276         }else{
33277             this.completeDrop(dropEvent);
33278         }
33279         return true;
33280     },
33281     
33282     completeDrop : function(de){
33283         var ns = de.dropNode, p = de.point, t = de.target;
33284         if(!(ns instanceof Array)){
33285             ns = [ns];
33286         }
33287         var n;
33288         for(var i = 0, len = ns.length; i < len; i++){
33289             n = ns[i];
33290             if(p == "above"){
33291                 t.parentNode.insertBefore(n, t);
33292             }else if(p == "below"){
33293                 t.parentNode.insertBefore(n, t.nextSibling);
33294             }else{
33295                 t.appendChild(n);
33296             }
33297         }
33298         n.ui.focus();
33299         if(this.tree.hlDrop){
33300             n.ui.highlight();
33301         }
33302         t.ui.endDrop();
33303         this.tree.fireEvent("nodedrop", de);
33304     },
33305     
33306     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33307         if(this.tree.hlDrop){
33308             dropNode.ui.focus();
33309             dropNode.ui.highlight();
33310         }
33311         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33312     },
33313     
33314     getTree : function(){
33315         return this.tree;
33316     },
33317     
33318     removeDropIndicators : function(n){
33319         if(n && n.ddel){
33320             var el = n.ddel;
33321             Roo.fly(el).removeClass([
33322                     "x-tree-drag-insert-above",
33323                     "x-tree-drag-insert-below",
33324                     "x-tree-drag-append"]);
33325             this.lastInsertClass = "_noclass";
33326         }
33327     },
33328     
33329     beforeDragDrop : function(target, e, id){
33330         this.cancelExpand();
33331         return true;
33332     },
33333     
33334     afterRepair : function(data){
33335         if(data && Roo.enableFx){
33336             data.node.ui.highlight();
33337         }
33338         this.hideProxy();
33339     }    
33340 });
33341
33342 }
33343 /*
33344  * Based on:
33345  * Ext JS Library 1.1.1
33346  * Copyright(c) 2006-2007, Ext JS, LLC.
33347  *
33348  * Originally Released Under LGPL - original licence link has changed is not relivant.
33349  *
33350  * Fork - LGPL
33351  * <script type="text/javascript">
33352  */
33353  
33354
33355 if(Roo.dd.DragZone){
33356 Roo.tree.TreeDragZone = function(tree, config){
33357     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33358     this.tree = tree;
33359 };
33360
33361 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33362     ddGroup : "TreeDD",
33363     
33364     onBeforeDrag : function(data, e){
33365         var n = data.node;
33366         return n && n.draggable && !n.disabled;
33367     },
33368     
33369     onInitDrag : function(e){
33370         var data = this.dragData;
33371         this.tree.getSelectionModel().select(data.node);
33372         this.proxy.update("");
33373         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33374         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33375     },
33376     
33377     getRepairXY : function(e, data){
33378         return data.node.ui.getDDRepairXY();
33379     },
33380     
33381     onEndDrag : function(data, e){
33382         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33383     },
33384     
33385     onValidDrop : function(dd, e, id){
33386         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33387         this.hideProxy();
33388     },
33389     
33390     beforeInvalidDrop : function(e, id){
33391         // this scrolls the original position back into view
33392         var sm = this.tree.getSelectionModel();
33393         sm.clearSelections();
33394         sm.select(this.dragData.node);
33395     }
33396 });
33397 }/*
33398  * Based on:
33399  * Ext JS Library 1.1.1
33400  * Copyright(c) 2006-2007, Ext JS, LLC.
33401  *
33402  * Originally Released Under LGPL - original licence link has changed is not relivant.
33403  *
33404  * Fork - LGPL
33405  * <script type="text/javascript">
33406  */
33407 /**
33408  * @class Roo.tree.TreeEditor
33409  * @extends Roo.Editor
33410  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33411  * as the editor field.
33412  * @constructor
33413  * @param {TreePanel} tree
33414  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33415  */
33416 Roo.tree.TreeEditor = function(tree, config){
33417     config = config || {};
33418     var field = config.events ? config : new Roo.form.TextField(config);
33419     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
33420
33421     this.tree = tree;
33422
33423     tree.on('beforeclick', this.beforeNodeClick, this);
33424     tree.getTreeEl().on('mousedown', this.hide, this);
33425     this.on('complete', this.updateNode, this);
33426     this.on('beforestartedit', this.fitToTree, this);
33427     this.on('startedit', this.bindScroll, this, {delay:10});
33428     this.on('specialkey', this.onSpecialKey, this);
33429 };
33430
33431 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33432     /**
33433      * @cfg {String} alignment
33434      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33435      */
33436     alignment: "l-l",
33437     // inherit
33438     autoSize: false,
33439     /**
33440      * @cfg {Boolean} hideEl
33441      * True to hide the bound element while the editor is displayed (defaults to false)
33442      */
33443     hideEl : false,
33444     /**
33445      * @cfg {String} cls
33446      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33447      */
33448     cls: "x-small-editor x-tree-editor",
33449     /**
33450      * @cfg {Boolean} shim
33451      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33452      */
33453     shim:false,
33454     // inherit
33455     shadow:"frame",
33456     /**
33457      * @cfg {Number} maxWidth
33458      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33459      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33460      * scroll and client offsets into account prior to each edit.
33461      */
33462     maxWidth: 250,
33463
33464     editDelay : 350,
33465
33466     // private
33467     fitToTree : function(ed, el){
33468         var td = this.tree.getTreeEl().dom, nd = el.dom;
33469         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33470             td.scrollLeft = nd.offsetLeft;
33471         }
33472         var w = Math.min(
33473                 this.maxWidth,
33474                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33475         this.setSize(w, '');
33476     },
33477
33478     // private
33479     triggerEdit : function(node){
33480         this.completeEdit();
33481         this.editNode = node;
33482         this.startEdit(node.ui.textNode, node.text);
33483     },
33484
33485     // private
33486     bindScroll : function(){
33487         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33488     },
33489
33490     // private
33491     beforeNodeClick : function(node, e){
33492         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33493         this.lastClick = new Date();
33494         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33495             e.stopEvent();
33496             this.triggerEdit(node);
33497             return false;
33498         }
33499     },
33500
33501     // private
33502     updateNode : function(ed, value){
33503         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33504         this.editNode.setText(value);
33505     },
33506
33507     // private
33508     onHide : function(){
33509         Roo.tree.TreeEditor.superclass.onHide.call(this);
33510         if(this.editNode){
33511             this.editNode.ui.focus();
33512         }
33513     },
33514
33515     // private
33516     onSpecialKey : function(field, e){
33517         var k = e.getKey();
33518         if(k == e.ESC){
33519             e.stopEvent();
33520             this.cancelEdit();
33521         }else if(k == e.ENTER && !e.hasModifier()){
33522             e.stopEvent();
33523             this.completeEdit();
33524         }
33525     }
33526 });//<Script type="text/javascript">
33527 /*
33528  * Based on:
33529  * Ext JS Library 1.1.1
33530  * Copyright(c) 2006-2007, Ext JS, LLC.
33531  *
33532  * Originally Released Under LGPL - original licence link has changed is not relivant.
33533  *
33534  * Fork - LGPL
33535  * <script type="text/javascript">
33536  */
33537  
33538 /**
33539  * Not documented??? - probably should be...
33540  */
33541
33542 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33543     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33544     
33545     renderElements : function(n, a, targetNode, bulkRender){
33546         //consel.log("renderElements?");
33547         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33548
33549         var t = n.getOwnerTree();
33550         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33551         
33552         var cols = t.columns;
33553         var bw = t.borderWidth;
33554         var c = cols[0];
33555         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33556          var cb = typeof a.checked == "boolean";
33557         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33558         var colcls = 'x-t-' + tid + '-c0';
33559         var buf = [
33560             '<li class="x-tree-node">',
33561             
33562                 
33563                 '<div class="x-tree-node-el ', a.cls,'">',
33564                     // extran...
33565                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33566                 
33567                 
33568                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33569                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33570                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33571                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33572                            (a.iconCls ? ' '+a.iconCls : ''),
33573                            '" unselectable="on" />',
33574                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33575                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33576                              
33577                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33578                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33579                             '<span unselectable="on" qtip="' + tx + '">',
33580                              tx,
33581                              '</span></a>' ,
33582                     '</div>',
33583                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33584                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33585                  ];
33586         for(var i = 1, len = cols.length; i < len; i++){
33587             c = cols[i];
33588             colcls = 'x-t-' + tid + '-c' +i;
33589             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33590             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33591                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33592                       "</div>");
33593          }
33594          
33595          buf.push(
33596             '</a>',
33597             '<div class="x-clear"></div></div>',
33598             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33599             "</li>");
33600         
33601         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33602             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33603                                 n.nextSibling.ui.getEl(), buf.join(""));
33604         }else{
33605             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33606         }
33607         var el = this.wrap.firstChild;
33608         this.elRow = el;
33609         this.elNode = el.firstChild;
33610         this.ranchor = el.childNodes[1];
33611         this.ctNode = this.wrap.childNodes[1];
33612         var cs = el.firstChild.childNodes;
33613         this.indentNode = cs[0];
33614         this.ecNode = cs[1];
33615         this.iconNode = cs[2];
33616         var index = 3;
33617         if(cb){
33618             this.checkbox = cs[3];
33619             index++;
33620         }
33621         this.anchor = cs[index];
33622         
33623         this.textNode = cs[index].firstChild;
33624         
33625         //el.on("click", this.onClick, this);
33626         //el.on("dblclick", this.onDblClick, this);
33627         
33628         
33629        // console.log(this);
33630     },
33631     initEvents : function(){
33632         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33633         
33634             
33635         var a = this.ranchor;
33636
33637         var el = Roo.get(a);
33638
33639         if(Roo.isOpera){ // opera render bug ignores the CSS
33640             el.setStyle("text-decoration", "none");
33641         }
33642
33643         el.on("click", this.onClick, this);
33644         el.on("dblclick", this.onDblClick, this);
33645         el.on("contextmenu", this.onContextMenu, this);
33646         
33647     },
33648     
33649     /*onSelectedChange : function(state){
33650         if(state){
33651             this.focus();
33652             this.addClass("x-tree-selected");
33653         }else{
33654             //this.blur();
33655             this.removeClass("x-tree-selected");
33656         }
33657     },*/
33658     addClass : function(cls){
33659         if(this.elRow){
33660             Roo.fly(this.elRow).addClass(cls);
33661         }
33662         
33663     },
33664     
33665     
33666     removeClass : function(cls){
33667         if(this.elRow){
33668             Roo.fly(this.elRow).removeClass(cls);
33669         }
33670     }
33671
33672     
33673     
33674 });//<Script type="text/javascript">
33675
33676 /*
33677  * Based on:
33678  * Ext JS Library 1.1.1
33679  * Copyright(c) 2006-2007, Ext JS, LLC.
33680  *
33681  * Originally Released Under LGPL - original licence link has changed is not relivant.
33682  *
33683  * Fork - LGPL
33684  * <script type="text/javascript">
33685  */
33686  
33687
33688 /**
33689  * @class Roo.tree.ColumnTree
33690  * @extends Roo.data.TreePanel
33691  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33692  * @cfg {int} borderWidth  compined right/left border allowance
33693  * @constructor
33694  * @param {String/HTMLElement/Element} el The container element
33695  * @param {Object} config
33696  */
33697 Roo.tree.ColumnTree =  function(el, config)
33698 {
33699    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33700    this.addEvents({
33701         /**
33702         * @event resize
33703         * Fire this event on a container when it resizes
33704         * @param {int} w Width
33705         * @param {int} h Height
33706         */
33707        "resize" : true
33708     });
33709     this.on('resize', this.onResize, this);
33710 };
33711
33712 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33713     //lines:false,
33714     
33715     
33716     borderWidth: Roo.isBorderBox ? 0 : 2, 
33717     headEls : false,
33718     
33719     render : function(){
33720         // add the header.....
33721        
33722         Roo.tree.ColumnTree.superclass.render.apply(this);
33723         
33724         this.el.addClass('x-column-tree');
33725         
33726         this.headers = this.el.createChild(
33727             {cls:'x-tree-headers'},this.innerCt.dom);
33728    
33729         var cols = this.columns, c;
33730         var totalWidth = 0;
33731         this.headEls = [];
33732         var  len = cols.length;
33733         for(var i = 0; i < len; i++){
33734              c = cols[i];
33735              totalWidth += c.width;
33736             this.headEls.push(this.headers.createChild({
33737                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33738                  cn: {
33739                      cls:'x-tree-hd-text',
33740                      html: c.header
33741                  },
33742                  style:'width:'+(c.width-this.borderWidth)+'px;'
33743              }));
33744         }
33745         this.headers.createChild({cls:'x-clear'});
33746         // prevent floats from wrapping when clipped
33747         this.headers.setWidth(totalWidth);
33748         //this.innerCt.setWidth(totalWidth);
33749         this.innerCt.setStyle({ overflow: 'auto' });
33750         this.onResize(this.width, this.height);
33751              
33752         
33753     },
33754     onResize : function(w,h)
33755     {
33756         this.height = h;
33757         this.width = w;
33758         // resize cols..
33759         this.innerCt.setWidth(this.width);
33760         this.innerCt.setHeight(this.height-20);
33761         
33762         // headers...
33763         var cols = this.columns, c;
33764         var totalWidth = 0;
33765         var expEl = false;
33766         var len = cols.length;
33767         for(var i = 0; i < len; i++){
33768             c = cols[i];
33769             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33770                 // it's the expander..
33771                 expEl  = this.headEls[i];
33772                 continue;
33773             }
33774             totalWidth += c.width;
33775             
33776         }
33777         if (expEl) {
33778             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33779         }
33780         this.headers.setWidth(w-20);
33781
33782         
33783         
33784         
33785     }
33786 });
33787 /*
33788  * Based on:
33789  * Ext JS Library 1.1.1
33790  * Copyright(c) 2006-2007, Ext JS, LLC.
33791  *
33792  * Originally Released Under LGPL - original licence link has changed is not relivant.
33793  *
33794  * Fork - LGPL
33795  * <script type="text/javascript">
33796  */
33797  
33798 /**
33799  * @class Roo.menu.Menu
33800  * @extends Roo.util.Observable
33801  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33802  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33803  * @constructor
33804  * Creates a new Menu
33805  * @param {Object} config Configuration options
33806  */
33807 Roo.menu.Menu = function(config){
33808     Roo.apply(this, config);
33809     this.id = this.id || Roo.id();
33810     this.addEvents({
33811         /**
33812          * @event beforeshow
33813          * Fires before this menu is displayed
33814          * @param {Roo.menu.Menu} this
33815          */
33816         beforeshow : true,
33817         /**
33818          * @event beforehide
33819          * Fires before this menu is hidden
33820          * @param {Roo.menu.Menu} this
33821          */
33822         beforehide : true,
33823         /**
33824          * @event show
33825          * Fires after this menu is displayed
33826          * @param {Roo.menu.Menu} this
33827          */
33828         show : true,
33829         /**
33830          * @event hide
33831          * Fires after this menu is hidden
33832          * @param {Roo.menu.Menu} this
33833          */
33834         hide : true,
33835         /**
33836          * @event click
33837          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33838          * @param {Roo.menu.Menu} this
33839          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33840          * @param {Roo.EventObject} e
33841          */
33842         click : true,
33843         /**
33844          * @event mouseover
33845          * Fires when the mouse is hovering over this menu
33846          * @param {Roo.menu.Menu} this
33847          * @param {Roo.EventObject} e
33848          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33849          */
33850         mouseover : true,
33851         /**
33852          * @event mouseout
33853          * Fires when the mouse exits this menu
33854          * @param {Roo.menu.Menu} this
33855          * @param {Roo.EventObject} e
33856          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33857          */
33858         mouseout : true,
33859         /**
33860          * @event itemclick
33861          * Fires when a menu item contained in this menu is clicked
33862          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33863          * @param {Roo.EventObject} e
33864          */
33865         itemclick: true
33866     });
33867     if (this.registerMenu) {
33868         Roo.menu.MenuMgr.register(this);
33869     }
33870     
33871     var mis = this.items;
33872     this.items = new Roo.util.MixedCollection();
33873     if(mis){
33874         this.add.apply(this, mis);
33875     }
33876 };
33877
33878 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33879     /**
33880      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33881      */
33882     minWidth : 120,
33883     /**
33884      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33885      * for bottom-right shadow (defaults to "sides")
33886      */
33887     shadow : "sides",
33888     /**
33889      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33890      * this menu (defaults to "tl-tr?")
33891      */
33892     subMenuAlign : "tl-tr?",
33893     /**
33894      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33895      * relative to its element of origin (defaults to "tl-bl?")
33896      */
33897     defaultAlign : "tl-bl?",
33898     /**
33899      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33900      */
33901     allowOtherMenus : false,
33902     /**
33903      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33904      */
33905     registerMenu : true,
33906
33907     hidden:true,
33908
33909     // private
33910     render : function(){
33911         if(this.el){
33912             return;
33913         }
33914         var el = this.el = new Roo.Layer({
33915             cls: "x-menu",
33916             shadow:this.shadow,
33917             constrain: false,
33918             parentEl: this.parentEl || document.body,
33919             zindex:15000
33920         });
33921
33922         this.keyNav = new Roo.menu.MenuNav(this);
33923
33924         if(this.plain){
33925             el.addClass("x-menu-plain");
33926         }
33927         if(this.cls){
33928             el.addClass(this.cls);
33929         }
33930         // generic focus element
33931         this.focusEl = el.createChild({
33932             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
33933         });
33934         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
33935         ul.on("click", this.onClick, this);
33936         ul.on("mouseover", this.onMouseOver, this);
33937         ul.on("mouseout", this.onMouseOut, this);
33938         this.items.each(function(item){
33939             var li = document.createElement("li");
33940             li.className = "x-menu-list-item";
33941             ul.dom.appendChild(li);
33942             item.render(li, this);
33943         }, this);
33944         this.ul = ul;
33945         this.autoWidth();
33946     },
33947
33948     // private
33949     autoWidth : function(){
33950         var el = this.el, ul = this.ul;
33951         if(!el){
33952             return;
33953         }
33954         var w = this.width;
33955         if(w){
33956             el.setWidth(w);
33957         }else if(Roo.isIE){
33958             el.setWidth(this.minWidth);
33959             var t = el.dom.offsetWidth; // force recalc
33960             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
33961         }
33962     },
33963
33964     // private
33965     delayAutoWidth : function(){
33966         if(this.rendered){
33967             if(!this.awTask){
33968                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
33969             }
33970             this.awTask.delay(20);
33971         }
33972     },
33973
33974     // private
33975     findTargetItem : function(e){
33976         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
33977         if(t && t.menuItemId){
33978             return this.items.get(t.menuItemId);
33979         }
33980     },
33981
33982     // private
33983     onClick : function(e){
33984         var t;
33985         if(t = this.findTargetItem(e)){
33986             t.onClick(e);
33987             this.fireEvent("click", this, t, e);
33988         }
33989     },
33990
33991     // private
33992     setActiveItem : function(item, autoExpand){
33993         if(item != this.activeItem){
33994             if(this.activeItem){
33995                 this.activeItem.deactivate();
33996             }
33997             this.activeItem = item;
33998             item.activate(autoExpand);
33999         }else if(autoExpand){
34000             item.expandMenu();
34001         }
34002     },
34003
34004     // private
34005     tryActivate : function(start, step){
34006         var items = this.items;
34007         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34008             var item = items.get(i);
34009             if(!item.disabled && item.canActivate){
34010                 this.setActiveItem(item, false);
34011                 return item;
34012             }
34013         }
34014         return false;
34015     },
34016
34017     // private
34018     onMouseOver : function(e){
34019         var t;
34020         if(t = this.findTargetItem(e)){
34021             if(t.canActivate && !t.disabled){
34022                 this.setActiveItem(t, true);
34023             }
34024         }
34025         this.fireEvent("mouseover", this, e, t);
34026     },
34027
34028     // private
34029     onMouseOut : function(e){
34030         var t;
34031         if(t = this.findTargetItem(e)){
34032             if(t == this.activeItem && t.shouldDeactivate(e)){
34033                 this.activeItem.deactivate();
34034                 delete this.activeItem;
34035             }
34036         }
34037         this.fireEvent("mouseout", this, e, t);
34038     },
34039
34040     /**
34041      * Read-only.  Returns true if the menu is currently displayed, else false.
34042      * @type Boolean
34043      */
34044     isVisible : function(){
34045         return this.el && !this.hidden;
34046     },
34047
34048     /**
34049      * Displays this menu relative to another element
34050      * @param {String/HTMLElement/Roo.Element} element The element to align to
34051      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34052      * the element (defaults to this.defaultAlign)
34053      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34054      */
34055     show : function(el, pos, parentMenu){
34056         this.parentMenu = parentMenu;
34057         if(!this.el){
34058             this.render();
34059         }
34060         this.fireEvent("beforeshow", this);
34061         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34062     },
34063
34064     /**
34065      * Displays this menu at a specific xy position
34066      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34067      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34068      */
34069     showAt : function(xy, parentMenu, /* private: */_e){
34070         this.parentMenu = parentMenu;
34071         if(!this.el){
34072             this.render();
34073         }
34074         if(_e !== false){
34075             this.fireEvent("beforeshow", this);
34076             xy = this.el.adjustForConstraints(xy);
34077         }
34078         this.el.setXY(xy);
34079         this.el.show();
34080         this.hidden = false;
34081         this.focus();
34082         this.fireEvent("show", this);
34083     },
34084
34085     focus : function(){
34086         if(!this.hidden){
34087             this.doFocus.defer(50, this);
34088         }
34089     },
34090
34091     doFocus : function(){
34092         if(!this.hidden){
34093             this.focusEl.focus();
34094         }
34095     },
34096
34097     /**
34098      * Hides this menu and optionally all parent menus
34099      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34100      */
34101     hide : function(deep){
34102         if(this.el && this.isVisible()){
34103             this.fireEvent("beforehide", this);
34104             if(this.activeItem){
34105                 this.activeItem.deactivate();
34106                 this.activeItem = null;
34107             }
34108             this.el.hide();
34109             this.hidden = true;
34110             this.fireEvent("hide", this);
34111         }
34112         if(deep === true && this.parentMenu){
34113             this.parentMenu.hide(true);
34114         }
34115     },
34116
34117     /**
34118      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34119      * Any of the following are valid:
34120      * <ul>
34121      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34122      * <li>An HTMLElement object which will be converted to a menu item</li>
34123      * <li>A menu item config object that will be created as a new menu item</li>
34124      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34125      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34126      * </ul>
34127      * Usage:
34128      * <pre><code>
34129 // Create the menu
34130 var menu = new Roo.menu.Menu();
34131
34132 // Create a menu item to add by reference
34133 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34134
34135 // Add a bunch of items at once using different methods.
34136 // Only the last item added will be returned.
34137 var item = menu.add(
34138     menuItem,                // add existing item by ref
34139     'Dynamic Item',          // new TextItem
34140     '-',                     // new separator
34141     { text: 'Config Item' }  // new item by config
34142 );
34143 </code></pre>
34144      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34145      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34146      */
34147     add : function(){
34148         var a = arguments, l = a.length, item;
34149         for(var i = 0; i < l; i++){
34150             var el = a[i];
34151             if ((typeof(el) == "object") && el.xtype && el.xns) {
34152                 el = Roo.factory(el, Roo.menu);
34153             }
34154             
34155             if(el.render){ // some kind of Item
34156                 item = this.addItem(el);
34157             }else if(typeof el == "string"){ // string
34158                 if(el == "separator" || el == "-"){
34159                     item = this.addSeparator();
34160                 }else{
34161                     item = this.addText(el);
34162                 }
34163             }else if(el.tagName || el.el){ // element
34164                 item = this.addElement(el);
34165             }else if(typeof el == "object"){ // must be menu item config?
34166                 item = this.addMenuItem(el);
34167             }
34168         }
34169         return item;
34170     },
34171
34172     /**
34173      * Returns this menu's underlying {@link Roo.Element} object
34174      * @return {Roo.Element} The element
34175      */
34176     getEl : function(){
34177         if(!this.el){
34178             this.render();
34179         }
34180         return this.el;
34181     },
34182
34183     /**
34184      * Adds a separator bar to the menu
34185      * @return {Roo.menu.Item} The menu item that was added
34186      */
34187     addSeparator : function(){
34188         return this.addItem(new Roo.menu.Separator());
34189     },
34190
34191     /**
34192      * Adds an {@link Roo.Element} object to the menu
34193      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34194      * @return {Roo.menu.Item} The menu item that was added
34195      */
34196     addElement : function(el){
34197         return this.addItem(new Roo.menu.BaseItem(el));
34198     },
34199
34200     /**
34201      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34202      * @param {Roo.menu.Item} item The menu item to add
34203      * @return {Roo.menu.Item} The menu item that was added
34204      */
34205     addItem : function(item){
34206         this.items.add(item);
34207         if(this.ul){
34208             var li = document.createElement("li");
34209             li.className = "x-menu-list-item";
34210             this.ul.dom.appendChild(li);
34211             item.render(li, this);
34212             this.delayAutoWidth();
34213         }
34214         return item;
34215     },
34216
34217     /**
34218      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34219      * @param {Object} config A MenuItem config object
34220      * @return {Roo.menu.Item} The menu item that was added
34221      */
34222     addMenuItem : function(config){
34223         if(!(config instanceof Roo.menu.Item)){
34224             if(typeof config.checked == "boolean"){ // must be check menu item config?
34225                 config = new Roo.menu.CheckItem(config);
34226             }else{
34227                 config = new Roo.menu.Item(config);
34228             }
34229         }
34230         return this.addItem(config);
34231     },
34232
34233     /**
34234      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34235      * @param {String} text The text to display in the menu item
34236      * @return {Roo.menu.Item} The menu item that was added
34237      */
34238     addText : function(text){
34239         return this.addItem(new Roo.menu.TextItem({ text : text }));
34240     },
34241
34242     /**
34243      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34244      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34245      * @param {Roo.menu.Item} item The menu item to add
34246      * @return {Roo.menu.Item} The menu item that was added
34247      */
34248     insert : function(index, item){
34249         this.items.insert(index, item);
34250         if(this.ul){
34251             var li = document.createElement("li");
34252             li.className = "x-menu-list-item";
34253             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34254             item.render(li, this);
34255             this.delayAutoWidth();
34256         }
34257         return item;
34258     },
34259
34260     /**
34261      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34262      * @param {Roo.menu.Item} item The menu item to remove
34263      */
34264     remove : function(item){
34265         this.items.removeKey(item.id);
34266         item.destroy();
34267     },
34268
34269     /**
34270      * Removes and destroys all items in the menu
34271      */
34272     removeAll : function(){
34273         var f;
34274         while(f = this.items.first()){
34275             this.remove(f);
34276         }
34277     }
34278 });
34279
34280 // MenuNav is a private utility class used internally by the Menu
34281 Roo.menu.MenuNav = function(menu){
34282     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34283     this.scope = this.menu = menu;
34284 };
34285
34286 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34287     doRelay : function(e, h){
34288         var k = e.getKey();
34289         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34290             this.menu.tryActivate(0, 1);
34291             return false;
34292         }
34293         return h.call(this.scope || this, e, this.menu);
34294     },
34295
34296     up : function(e, m){
34297         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34298             m.tryActivate(m.items.length-1, -1);
34299         }
34300     },
34301
34302     down : function(e, m){
34303         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34304             m.tryActivate(0, 1);
34305         }
34306     },
34307
34308     right : function(e, m){
34309         if(m.activeItem){
34310             m.activeItem.expandMenu(true);
34311         }
34312     },
34313
34314     left : function(e, m){
34315         m.hide();
34316         if(m.parentMenu && m.parentMenu.activeItem){
34317             m.parentMenu.activeItem.activate();
34318         }
34319     },
34320
34321     enter : function(e, m){
34322         if(m.activeItem){
34323             e.stopPropagation();
34324             m.activeItem.onClick(e);
34325             m.fireEvent("click", this, m.activeItem);
34326             return true;
34327         }
34328     }
34329 });/*
34330  * Based on:
34331  * Ext JS Library 1.1.1
34332  * Copyright(c) 2006-2007, Ext JS, LLC.
34333  *
34334  * Originally Released Under LGPL - original licence link has changed is not relivant.
34335  *
34336  * Fork - LGPL
34337  * <script type="text/javascript">
34338  */
34339  
34340 /**
34341  * @class Roo.menu.MenuMgr
34342  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34343  * @singleton
34344  */
34345 Roo.menu.MenuMgr = function(){
34346    var menus, active, groups = {}, attached = false, lastShow = new Date();
34347
34348    // private - called when first menu is created
34349    function init(){
34350        menus = {};
34351        active = new Roo.util.MixedCollection();
34352        Roo.get(document).addKeyListener(27, function(){
34353            if(active.length > 0){
34354                hideAll();
34355            }
34356        });
34357    }
34358
34359    // private
34360    function hideAll(){
34361        if(active && active.length > 0){
34362            var c = active.clone();
34363            c.each(function(m){
34364                m.hide();
34365            });
34366        }
34367    }
34368
34369    // private
34370    function onHide(m){
34371        active.remove(m);
34372        if(active.length < 1){
34373            Roo.get(document).un("mousedown", onMouseDown);
34374            attached = false;
34375        }
34376    }
34377
34378    // private
34379    function onShow(m){
34380        var last = active.last();
34381        lastShow = new Date();
34382        active.add(m);
34383        if(!attached){
34384            Roo.get(document).on("mousedown", onMouseDown);
34385            attached = true;
34386        }
34387        if(m.parentMenu){
34388           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34389           m.parentMenu.activeChild = m;
34390        }else if(last && last.isVisible()){
34391           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34392        }
34393    }
34394
34395    // private
34396    function onBeforeHide(m){
34397        if(m.activeChild){
34398            m.activeChild.hide();
34399        }
34400        if(m.autoHideTimer){
34401            clearTimeout(m.autoHideTimer);
34402            delete m.autoHideTimer;
34403        }
34404    }
34405
34406    // private
34407    function onBeforeShow(m){
34408        var pm = m.parentMenu;
34409        if(!pm && !m.allowOtherMenus){
34410            hideAll();
34411        }else if(pm && pm.activeChild && active != m){
34412            pm.activeChild.hide();
34413        }
34414    }
34415
34416    // private
34417    function onMouseDown(e){
34418        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34419            hideAll();
34420        }
34421    }
34422
34423    // private
34424    function onBeforeCheck(mi, state){
34425        if(state){
34426            var g = groups[mi.group];
34427            for(var i = 0, l = g.length; i < l; i++){
34428                if(g[i] != mi){
34429                    g[i].setChecked(false);
34430                }
34431            }
34432        }
34433    }
34434
34435    return {
34436
34437        /**
34438         * Hides all menus that are currently visible
34439         */
34440        hideAll : function(){
34441             hideAll();  
34442        },
34443
34444        // private
34445        register : function(menu){
34446            if(!menus){
34447                init();
34448            }
34449            menus[menu.id] = menu;
34450            menu.on("beforehide", onBeforeHide);
34451            menu.on("hide", onHide);
34452            menu.on("beforeshow", onBeforeShow);
34453            menu.on("show", onShow);
34454            var g = menu.group;
34455            if(g && menu.events["checkchange"]){
34456                if(!groups[g]){
34457                    groups[g] = [];
34458                }
34459                groups[g].push(menu);
34460                menu.on("checkchange", onCheck);
34461            }
34462        },
34463
34464         /**
34465          * Returns a {@link Roo.menu.Menu} object
34466          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34467          * be used to generate and return a new Menu instance.
34468          */
34469        get : function(menu){
34470            if(typeof menu == "string"){ // menu id
34471                return menus[menu];
34472            }else if(menu.events){  // menu instance
34473                return menu;
34474            }else if(typeof menu.length == 'number'){ // array of menu items?
34475                return new Roo.menu.Menu({items:menu});
34476            }else{ // otherwise, must be a config
34477                return new Roo.menu.Menu(menu);
34478            }
34479        },
34480
34481        // private
34482        unregister : function(menu){
34483            delete menus[menu.id];
34484            menu.un("beforehide", onBeforeHide);
34485            menu.un("hide", onHide);
34486            menu.un("beforeshow", onBeforeShow);
34487            menu.un("show", onShow);
34488            var g = menu.group;
34489            if(g && menu.events["checkchange"]){
34490                groups[g].remove(menu);
34491                menu.un("checkchange", onCheck);
34492            }
34493        },
34494
34495        // private
34496        registerCheckable : function(menuItem){
34497            var g = menuItem.group;
34498            if(g){
34499                if(!groups[g]){
34500                    groups[g] = [];
34501                }
34502                groups[g].push(menuItem);
34503                menuItem.on("beforecheckchange", onBeforeCheck);
34504            }
34505        },
34506
34507        // private
34508        unregisterCheckable : function(menuItem){
34509            var g = menuItem.group;
34510            if(g){
34511                groups[g].remove(menuItem);
34512                menuItem.un("beforecheckchange", onBeforeCheck);
34513            }
34514        }
34515    };
34516 }();/*
34517  * Based on:
34518  * Ext JS Library 1.1.1
34519  * Copyright(c) 2006-2007, Ext JS, LLC.
34520  *
34521  * Originally Released Under LGPL - original licence link has changed is not relivant.
34522  *
34523  * Fork - LGPL
34524  * <script type="text/javascript">
34525  */
34526  
34527
34528 /**
34529  * @class Roo.menu.BaseItem
34530  * @extends Roo.Component
34531  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34532  * management and base configuration options shared by all menu components.
34533  * @constructor
34534  * Creates a new BaseItem
34535  * @param {Object} config Configuration options
34536  */
34537 Roo.menu.BaseItem = function(config){
34538     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34539
34540     this.addEvents({
34541         /**
34542          * @event click
34543          * Fires when this item is clicked
34544          * @param {Roo.menu.BaseItem} this
34545          * @param {Roo.EventObject} e
34546          */
34547         click: true,
34548         /**
34549          * @event activate
34550          * Fires when this item is activated
34551          * @param {Roo.menu.BaseItem} this
34552          */
34553         activate : true,
34554         /**
34555          * @event deactivate
34556          * Fires when this item is deactivated
34557          * @param {Roo.menu.BaseItem} this
34558          */
34559         deactivate : true
34560     });
34561
34562     if(this.handler){
34563         this.on("click", this.handler, this.scope, true);
34564     }
34565 };
34566
34567 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34568     /**
34569      * @cfg {Function} handler
34570      * A function that will handle the click event of this menu item (defaults to undefined)
34571      */
34572     /**
34573      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34574      */
34575     canActivate : false,
34576     /**
34577      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34578      */
34579     activeClass : "x-menu-item-active",
34580     /**
34581      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34582      */
34583     hideOnClick : true,
34584     /**
34585      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34586      */
34587     hideDelay : 100,
34588
34589     // private
34590     ctype: "Roo.menu.BaseItem",
34591
34592     // private
34593     actionMode : "container",
34594
34595     // private
34596     render : function(container, parentMenu){
34597         this.parentMenu = parentMenu;
34598         Roo.menu.BaseItem.superclass.render.call(this, container);
34599         this.container.menuItemId = this.id;
34600     },
34601
34602     // private
34603     onRender : function(container, position){
34604         this.el = Roo.get(this.el);
34605         container.dom.appendChild(this.el.dom);
34606     },
34607
34608     // private
34609     onClick : function(e){
34610         if(!this.disabled && this.fireEvent("click", this, e) !== false
34611                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34612             this.handleClick(e);
34613         }else{
34614             e.stopEvent();
34615         }
34616     },
34617
34618     // private
34619     activate : function(){
34620         if(this.disabled){
34621             return false;
34622         }
34623         var li = this.container;
34624         li.addClass(this.activeClass);
34625         this.region = li.getRegion().adjust(2, 2, -2, -2);
34626         this.fireEvent("activate", this);
34627         return true;
34628     },
34629
34630     // private
34631     deactivate : function(){
34632         this.container.removeClass(this.activeClass);
34633         this.fireEvent("deactivate", this);
34634     },
34635
34636     // private
34637     shouldDeactivate : function(e){
34638         return !this.region || !this.region.contains(e.getPoint());
34639     },
34640
34641     // private
34642     handleClick : function(e){
34643         if(this.hideOnClick){
34644             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34645         }
34646     },
34647
34648     // private
34649     expandMenu : function(autoActivate){
34650         // do nothing
34651     },
34652
34653     // private
34654     hideMenu : function(){
34655         // do nothing
34656     }
34657 });/*
34658  * Based on:
34659  * Ext JS Library 1.1.1
34660  * Copyright(c) 2006-2007, Ext JS, LLC.
34661  *
34662  * Originally Released Under LGPL - original licence link has changed is not relivant.
34663  *
34664  * Fork - LGPL
34665  * <script type="text/javascript">
34666  */
34667  
34668 /**
34669  * @class Roo.menu.Adapter
34670  * @extends Roo.menu.BaseItem
34671  * 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.
34672  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34673  * @constructor
34674  * Creates a new Adapter
34675  * @param {Object} config Configuration options
34676  */
34677 Roo.menu.Adapter = function(component, config){
34678     Roo.menu.Adapter.superclass.constructor.call(this, config);
34679     this.component = component;
34680 };
34681 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34682     // private
34683     canActivate : true,
34684
34685     // private
34686     onRender : function(container, position){
34687         this.component.render(container);
34688         this.el = this.component.getEl();
34689     },
34690
34691     // private
34692     activate : function(){
34693         if(this.disabled){
34694             return false;
34695         }
34696         this.component.focus();
34697         this.fireEvent("activate", this);
34698         return true;
34699     },
34700
34701     // private
34702     deactivate : function(){
34703         this.fireEvent("deactivate", this);
34704     },
34705
34706     // private
34707     disable : function(){
34708         this.component.disable();
34709         Roo.menu.Adapter.superclass.disable.call(this);
34710     },
34711
34712     // private
34713     enable : function(){
34714         this.component.enable();
34715         Roo.menu.Adapter.superclass.enable.call(this);
34716     }
34717 });/*
34718  * Based on:
34719  * Ext JS Library 1.1.1
34720  * Copyright(c) 2006-2007, Ext JS, LLC.
34721  *
34722  * Originally Released Under LGPL - original licence link has changed is not relivant.
34723  *
34724  * Fork - LGPL
34725  * <script type="text/javascript">
34726  */
34727
34728 /**
34729  * @class Roo.menu.TextItem
34730  * @extends Roo.menu.BaseItem
34731  * Adds a static text string to a menu, usually used as either a heading or group separator.
34732  * Note: old style constructor with text is still supported.
34733  * 
34734  * @constructor
34735  * Creates a new TextItem
34736  * @param {Object} cfg Configuration
34737  */
34738 Roo.menu.TextItem = function(cfg){
34739     if (typeof(cfg) == 'string') {
34740         this.text = cfg;
34741     } else {
34742         Roo.apply(this,cfg);
34743     }
34744     
34745     Roo.menu.TextItem.superclass.constructor.call(this);
34746 };
34747
34748 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34749     /**
34750      * @cfg {Boolean} text Text to show on item.
34751      */
34752     text : '',
34753     
34754     /**
34755      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34756      */
34757     hideOnClick : false,
34758     /**
34759      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34760      */
34761     itemCls : "x-menu-text",
34762
34763     // private
34764     onRender : function(){
34765         var s = document.createElement("span");
34766         s.className = this.itemCls;
34767         s.innerHTML = this.text;
34768         this.el = s;
34769         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34770     }
34771 });/*
34772  * Based on:
34773  * Ext JS Library 1.1.1
34774  * Copyright(c) 2006-2007, Ext JS, LLC.
34775  *
34776  * Originally Released Under LGPL - original licence link has changed is not relivant.
34777  *
34778  * Fork - LGPL
34779  * <script type="text/javascript">
34780  */
34781
34782 /**
34783  * @class Roo.menu.Separator
34784  * @extends Roo.menu.BaseItem
34785  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34786  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34787  * @constructor
34788  * @param {Object} config Configuration options
34789  */
34790 Roo.menu.Separator = function(config){
34791     Roo.menu.Separator.superclass.constructor.call(this, config);
34792 };
34793
34794 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34795     /**
34796      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34797      */
34798     itemCls : "x-menu-sep",
34799     /**
34800      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34801      */
34802     hideOnClick : false,
34803
34804     // private
34805     onRender : function(li){
34806         var s = document.createElement("span");
34807         s.className = this.itemCls;
34808         s.innerHTML = "&#160;";
34809         this.el = s;
34810         li.addClass("x-menu-sep-li");
34811         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34812     }
34813 });/*
34814  * Based on:
34815  * Ext JS Library 1.1.1
34816  * Copyright(c) 2006-2007, Ext JS, LLC.
34817  *
34818  * Originally Released Under LGPL - original licence link has changed is not relivant.
34819  *
34820  * Fork - LGPL
34821  * <script type="text/javascript">
34822  */
34823 /**
34824  * @class Roo.menu.Item
34825  * @extends Roo.menu.BaseItem
34826  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34827  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34828  * activation and click handling.
34829  * @constructor
34830  * Creates a new Item
34831  * @param {Object} config Configuration options
34832  */
34833 Roo.menu.Item = function(config){
34834     Roo.menu.Item.superclass.constructor.call(this, config);
34835     if(this.menu){
34836         this.menu = Roo.menu.MenuMgr.get(this.menu);
34837     }
34838 };
34839 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34840     
34841     /**
34842      * @cfg {String} text
34843      * The text to show on the menu item.
34844      */
34845     text: '',
34846      /**
34847      * @cfg {String} HTML to render in menu
34848      * The text to show on the menu item (HTML version).
34849      */
34850     html: '',
34851     /**
34852      * @cfg {String} icon
34853      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34854      */
34855     icon: undefined,
34856     /**
34857      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34858      */
34859     itemCls : "x-menu-item",
34860     /**
34861      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34862      */
34863     canActivate : true,
34864     /**
34865      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34866      */
34867     showDelay: 200,
34868     // doc'd in BaseItem
34869     hideDelay: 200,
34870
34871     // private
34872     ctype: "Roo.menu.Item",
34873     
34874     // private
34875     onRender : function(container, position){
34876         var el = document.createElement("a");
34877         el.hideFocus = true;
34878         el.unselectable = "on";
34879         el.href = this.href || "#";
34880         if(this.hrefTarget){
34881             el.target = this.hrefTarget;
34882         }
34883         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34884         
34885         var html = this.html.length ? this.html  : String.format('{0}',this.text);
34886         
34887         el.innerHTML = String.format(
34888                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
34889                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
34890         this.el = el;
34891         Roo.menu.Item.superclass.onRender.call(this, container, position);
34892     },
34893
34894     /**
34895      * Sets the text to display in this menu item
34896      * @param {String} text The text to display
34897      * @param {Boolean} isHTML true to indicate text is pure html.
34898      */
34899     setText : function(text, isHTML){
34900         if (isHTML) {
34901             this.html = text;
34902         } else {
34903             this.text = text;
34904             this.html = '';
34905         }
34906         if(this.rendered){
34907             var html = this.html.length ? this.html  : String.format('{0}',this.text);
34908      
34909             this.el.update(String.format(
34910                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
34911                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34912             this.parentMenu.autoWidth();
34913         }
34914     },
34915
34916     // private
34917     handleClick : function(e){
34918         if(!this.href){ // if no link defined, stop the event automatically
34919             e.stopEvent();
34920         }
34921         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34922     },
34923
34924     // private
34925     activate : function(autoExpand){
34926         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
34927             this.focus();
34928             if(autoExpand){
34929                 this.expandMenu();
34930             }
34931         }
34932         return true;
34933     },
34934
34935     // private
34936     shouldDeactivate : function(e){
34937         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
34938             if(this.menu && this.menu.isVisible()){
34939                 return !this.menu.getEl().getRegion().contains(e.getPoint());
34940             }
34941             return true;
34942         }
34943         return false;
34944     },
34945
34946     // private
34947     deactivate : function(){
34948         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
34949         this.hideMenu();
34950     },
34951
34952     // private
34953     expandMenu : function(autoActivate){
34954         if(!this.disabled && this.menu){
34955             clearTimeout(this.hideTimer);
34956             delete this.hideTimer;
34957             if(!this.menu.isVisible() && !this.showTimer){
34958                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
34959             }else if (this.menu.isVisible() && autoActivate){
34960                 this.menu.tryActivate(0, 1);
34961             }
34962         }
34963     },
34964
34965     // private
34966     deferExpand : function(autoActivate){
34967         delete this.showTimer;
34968         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
34969         if(autoActivate){
34970             this.menu.tryActivate(0, 1);
34971         }
34972     },
34973
34974     // private
34975     hideMenu : function(){
34976         clearTimeout(this.showTimer);
34977         delete this.showTimer;
34978         if(!this.hideTimer && this.menu && this.menu.isVisible()){
34979             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
34980         }
34981     },
34982
34983     // private
34984     deferHide : function(){
34985         delete this.hideTimer;
34986         this.menu.hide();
34987     }
34988 });/*
34989  * Based on:
34990  * Ext JS Library 1.1.1
34991  * Copyright(c) 2006-2007, Ext JS, LLC.
34992  *
34993  * Originally Released Under LGPL - original licence link has changed is not relivant.
34994  *
34995  * Fork - LGPL
34996  * <script type="text/javascript">
34997  */
34998  
34999 /**
35000  * @class Roo.menu.CheckItem
35001  * @extends Roo.menu.Item
35002  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35003  * @constructor
35004  * Creates a new CheckItem
35005  * @param {Object} config Configuration options
35006  */
35007 Roo.menu.CheckItem = function(config){
35008     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35009     this.addEvents({
35010         /**
35011          * @event beforecheckchange
35012          * Fires before the checked value is set, providing an opportunity to cancel if needed
35013          * @param {Roo.menu.CheckItem} this
35014          * @param {Boolean} checked The new checked value that will be set
35015          */
35016         "beforecheckchange" : true,
35017         /**
35018          * @event checkchange
35019          * Fires after the checked value has been set
35020          * @param {Roo.menu.CheckItem} this
35021          * @param {Boolean} checked The checked value that was set
35022          */
35023         "checkchange" : true
35024     });
35025     if(this.checkHandler){
35026         this.on('checkchange', this.checkHandler, this.scope);
35027     }
35028 };
35029 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35030     /**
35031      * @cfg {String} group
35032      * All check items with the same group name will automatically be grouped into a single-select
35033      * radio button group (defaults to '')
35034      */
35035     /**
35036      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35037      */
35038     itemCls : "x-menu-item x-menu-check-item",
35039     /**
35040      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35041      */
35042     groupClass : "x-menu-group-item",
35043
35044     /**
35045      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35046      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35047      * initialized with checked = true will be rendered as checked.
35048      */
35049     checked: false,
35050
35051     // private
35052     ctype: "Roo.menu.CheckItem",
35053
35054     // private
35055     onRender : function(c){
35056         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35057         if(this.group){
35058             this.el.addClass(this.groupClass);
35059         }
35060         Roo.menu.MenuMgr.registerCheckable(this);
35061         if(this.checked){
35062             this.checked = false;
35063             this.setChecked(true, true);
35064         }
35065     },
35066
35067     // private
35068     destroy : function(){
35069         if(this.rendered){
35070             Roo.menu.MenuMgr.unregisterCheckable(this);
35071         }
35072         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35073     },
35074
35075     /**
35076      * Set the checked state of this item
35077      * @param {Boolean} checked The new checked value
35078      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35079      */
35080     setChecked : function(state, suppressEvent){
35081         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35082             if(this.container){
35083                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35084             }
35085             this.checked = state;
35086             if(suppressEvent !== true){
35087                 this.fireEvent("checkchange", this, state);
35088             }
35089         }
35090     },
35091
35092     // private
35093     handleClick : function(e){
35094        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35095            this.setChecked(!this.checked);
35096        }
35097        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35098     }
35099 });/*
35100  * Based on:
35101  * Ext JS Library 1.1.1
35102  * Copyright(c) 2006-2007, Ext JS, LLC.
35103  *
35104  * Originally Released Under LGPL - original licence link has changed is not relivant.
35105  *
35106  * Fork - LGPL
35107  * <script type="text/javascript">
35108  */
35109  
35110 /**
35111  * @class Roo.menu.DateItem
35112  * @extends Roo.menu.Adapter
35113  * A menu item that wraps the {@link Roo.DatPicker} component.
35114  * @constructor
35115  * Creates a new DateItem
35116  * @param {Object} config Configuration options
35117  */
35118 Roo.menu.DateItem = function(config){
35119     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35120     /** The Roo.DatePicker object @type Roo.DatePicker */
35121     this.picker = this.component;
35122     this.addEvents({select: true});
35123     
35124     this.picker.on("render", function(picker){
35125         picker.getEl().swallowEvent("click");
35126         picker.container.addClass("x-menu-date-item");
35127     });
35128
35129     this.picker.on("select", this.onSelect, this);
35130 };
35131
35132 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35133     // private
35134     onSelect : function(picker, date){
35135         this.fireEvent("select", this, date, picker);
35136         Roo.menu.DateItem.superclass.handleClick.call(this);
35137     }
35138 });/*
35139  * Based on:
35140  * Ext JS Library 1.1.1
35141  * Copyright(c) 2006-2007, Ext JS, LLC.
35142  *
35143  * Originally Released Under LGPL - original licence link has changed is not relivant.
35144  *
35145  * Fork - LGPL
35146  * <script type="text/javascript">
35147  */
35148  
35149 /**
35150  * @class Roo.menu.ColorItem
35151  * @extends Roo.menu.Adapter
35152  * A menu item that wraps the {@link Roo.ColorPalette} component.
35153  * @constructor
35154  * Creates a new ColorItem
35155  * @param {Object} config Configuration options
35156  */
35157 Roo.menu.ColorItem = function(config){
35158     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35159     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35160     this.palette = this.component;
35161     this.relayEvents(this.palette, ["select"]);
35162     if(this.selectHandler){
35163         this.on('select', this.selectHandler, this.scope);
35164     }
35165 };
35166 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35167  * Based on:
35168  * Ext JS Library 1.1.1
35169  * Copyright(c) 2006-2007, Ext JS, LLC.
35170  *
35171  * Originally Released Under LGPL - original licence link has changed is not relivant.
35172  *
35173  * Fork - LGPL
35174  * <script type="text/javascript">
35175  */
35176  
35177
35178 /**
35179  * @class Roo.menu.DateMenu
35180  * @extends Roo.menu.Menu
35181  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35182  * @constructor
35183  * Creates a new DateMenu
35184  * @param {Object} config Configuration options
35185  */
35186 Roo.menu.DateMenu = function(config){
35187     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35188     this.plain = true;
35189     var di = new Roo.menu.DateItem(config);
35190     this.add(di);
35191     /**
35192      * The {@link Roo.DatePicker} instance for this DateMenu
35193      * @type DatePicker
35194      */
35195     this.picker = di.picker;
35196     /**
35197      * @event select
35198      * @param {DatePicker} picker
35199      * @param {Date} date
35200      */
35201     this.relayEvents(di, ["select"]);
35202
35203     this.on('beforeshow', function(){
35204         if(this.picker){
35205             this.picker.hideMonthPicker(true);
35206         }
35207     }, this);
35208 };
35209 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35210     cls:'x-date-menu'
35211 });/*
35212  * Based on:
35213  * Ext JS Library 1.1.1
35214  * Copyright(c) 2006-2007, Ext JS, LLC.
35215  *
35216  * Originally Released Under LGPL - original licence link has changed is not relivant.
35217  *
35218  * Fork - LGPL
35219  * <script type="text/javascript">
35220  */
35221  
35222
35223 /**
35224  * @class Roo.menu.ColorMenu
35225  * @extends Roo.menu.Menu
35226  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35227  * @constructor
35228  * Creates a new ColorMenu
35229  * @param {Object} config Configuration options
35230  */
35231 Roo.menu.ColorMenu = function(config){
35232     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35233     this.plain = true;
35234     var ci = new Roo.menu.ColorItem(config);
35235     this.add(ci);
35236     /**
35237      * The {@link Roo.ColorPalette} instance for this ColorMenu
35238      * @type ColorPalette
35239      */
35240     this.palette = ci.palette;
35241     /**
35242      * @event select
35243      * @param {ColorPalette} palette
35244      * @param {String} color
35245      */
35246     this.relayEvents(ci, ["select"]);
35247 };
35248 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35249  * Based on:
35250  * Ext JS Library 1.1.1
35251  * Copyright(c) 2006-2007, Ext JS, LLC.
35252  *
35253  * Originally Released Under LGPL - original licence link has changed is not relivant.
35254  *
35255  * Fork - LGPL
35256  * <script type="text/javascript">
35257  */
35258  
35259 /**
35260  * @class Roo.form.Field
35261  * @extends Roo.BoxComponent
35262  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35263  * @constructor
35264  * Creates a new Field
35265  * @param {Object} config Configuration options
35266  */
35267 Roo.form.Field = function(config){
35268     Roo.form.Field.superclass.constructor.call(this, config);
35269 };
35270
35271 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35272     /**
35273      * @cfg {String} fieldLabel Label to use when rendering a form.
35274      */
35275        /**
35276      * @cfg {String} qtip Mouse over tip
35277      */
35278      
35279     /**
35280      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35281      */
35282     invalidClass : "x-form-invalid",
35283     /**
35284      * @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")
35285      */
35286     invalidText : "The value in this field is invalid",
35287     /**
35288      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35289      */
35290     focusClass : "x-form-focus",
35291     /**
35292      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35293       automatic validation (defaults to "keyup").
35294      */
35295     validationEvent : "keyup",
35296     /**
35297      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35298      */
35299     validateOnBlur : true,
35300     /**
35301      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35302      */
35303     validationDelay : 250,
35304     /**
35305      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35306      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35307      */
35308     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35309     /**
35310      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35311      */
35312     fieldClass : "x-form-field",
35313     /**
35314      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35315      *<pre>
35316 Value         Description
35317 -----------   ----------------------------------------------------------------------
35318 qtip          Display a quick tip when the user hovers over the field
35319 title         Display a default browser title attribute popup
35320 under         Add a block div beneath the field containing the error text
35321 side          Add an error icon to the right of the field with a popup on hover
35322 [element id]  Add the error text directly to the innerHTML of the specified element
35323 </pre>
35324      */
35325     msgTarget : 'qtip',
35326     /**
35327      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35328      */
35329     msgFx : 'normal',
35330
35331     /**
35332      * @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.
35333      */
35334     readOnly : false,
35335
35336     /**
35337      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35338      */
35339     disabled : false,
35340
35341     /**
35342      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35343      */
35344     inputType : undefined,
35345     
35346     /**
35347      * @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).
35348          */
35349         tabIndex : undefined,
35350         
35351     // private
35352     isFormField : true,
35353
35354     // private
35355     hasFocus : false,
35356     /**
35357      * @property {Roo.Element} fieldEl
35358      * Element Containing the rendered Field (with label etc.)
35359      */
35360     /**
35361      * @cfg {Mixed} value A value to initialize this field with.
35362      */
35363     value : undefined,
35364
35365     /**
35366      * @cfg {String} name The field's HTML name attribute.
35367      */
35368     /**
35369      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35370      */
35371
35372         // private ??
35373         initComponent : function(){
35374         Roo.form.Field.superclass.initComponent.call(this);
35375         this.addEvents({
35376             /**
35377              * @event focus
35378              * Fires when this field receives input focus.
35379              * @param {Roo.form.Field} this
35380              */
35381             focus : true,
35382             /**
35383              * @event blur
35384              * Fires when this field loses input focus.
35385              * @param {Roo.form.Field} this
35386              */
35387             blur : true,
35388             /**
35389              * @event specialkey
35390              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35391              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35392              * @param {Roo.form.Field} this
35393              * @param {Roo.EventObject} e The event object
35394              */
35395             specialkey : true,
35396             /**
35397              * @event change
35398              * Fires just before the field blurs if the field value has changed.
35399              * @param {Roo.form.Field} this
35400              * @param {Mixed} newValue The new value
35401              * @param {Mixed} oldValue The original value
35402              */
35403             change : true,
35404             /**
35405              * @event invalid
35406              * Fires after the field has been marked as invalid.
35407              * @param {Roo.form.Field} this
35408              * @param {String} msg The validation message
35409              */
35410             invalid : true,
35411             /**
35412              * @event valid
35413              * Fires after the field has been validated with no errors.
35414              * @param {Roo.form.Field} this
35415              */
35416             valid : true,
35417              /**
35418              * @event keyup
35419              * Fires after the key up
35420              * @param {Roo.form.Field} this
35421              * @param {Roo.EventObject}  e The event Object
35422              */
35423             keyup : true
35424         });
35425     },
35426
35427     /**
35428      * Returns the name attribute of the field if available
35429      * @return {String} name The field name
35430      */
35431     getName: function(){
35432          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35433     },
35434
35435     // private
35436     onRender : function(ct, position){
35437         Roo.form.Field.superclass.onRender.call(this, ct, position);
35438         if(!this.el){
35439             var cfg = this.getAutoCreate();
35440             if(!cfg.name){
35441                 cfg.name = this.name || this.id;
35442             }
35443             if(this.inputType){
35444                 cfg.type = this.inputType;
35445             }
35446             this.el = ct.createChild(cfg, position);
35447         }
35448         var type = this.el.dom.type;
35449         if(type){
35450             if(type == 'password'){
35451                 type = 'text';
35452             }
35453             this.el.addClass('x-form-'+type);
35454         }
35455         if(this.readOnly){
35456             this.el.dom.readOnly = true;
35457         }
35458         if(this.tabIndex !== undefined){
35459             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35460         }
35461
35462         this.el.addClass([this.fieldClass, this.cls]);
35463         this.initValue();
35464     },
35465
35466     /**
35467      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35468      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35469      * @return {Roo.form.Field} this
35470      */
35471     applyTo : function(target){
35472         this.allowDomMove = false;
35473         this.el = Roo.get(target);
35474         this.render(this.el.dom.parentNode);
35475         return this;
35476     },
35477
35478     // private
35479     initValue : function(){
35480         if(this.value !== undefined){
35481             this.setValue(this.value);
35482         }else if(this.el.dom.value.length > 0){
35483             this.setValue(this.el.dom.value);
35484         }
35485     },
35486
35487     /**
35488      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35489      */
35490     isDirty : function() {
35491         if(this.disabled) {
35492             return false;
35493         }
35494         return String(this.getValue()) !== String(this.originalValue);
35495     },
35496
35497     // private
35498     afterRender : function(){
35499         Roo.form.Field.superclass.afterRender.call(this);
35500         this.initEvents();
35501     },
35502
35503     // private
35504     fireKey : function(e){
35505         //Roo.log('field ' + e.getKey());
35506         if(e.isNavKeyPress()){
35507             this.fireEvent("specialkey", this, e);
35508         }
35509     },
35510
35511     /**
35512      * Resets the current field value to the originally loaded value and clears any validation messages
35513      */
35514     reset : function(){
35515         this.setValue(this.originalValue);
35516         this.clearInvalid();
35517     },
35518
35519     // private
35520     initEvents : function(){
35521         // safari killled keypress - so keydown is now used..
35522         this.el.on("keydown" , this.fireKey,  this);
35523         this.el.on("focus", this.onFocus,  this);
35524         this.el.on("blur", this.onBlur,  this);
35525         this.el.relayEvent('keyup', this);
35526
35527         // reference to original value for reset
35528         this.originalValue = this.getValue();
35529     },
35530
35531     // private
35532     onFocus : function(){
35533         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35534             this.el.addClass(this.focusClass);
35535         }
35536         if(!this.hasFocus){
35537             this.hasFocus = true;
35538             this.startValue = this.getValue();
35539             this.fireEvent("focus", this);
35540         }
35541     },
35542
35543     beforeBlur : Roo.emptyFn,
35544
35545     // private
35546     onBlur : function(){
35547         this.beforeBlur();
35548         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35549             this.el.removeClass(this.focusClass);
35550         }
35551         this.hasFocus = false;
35552         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35553             this.validate();
35554         }
35555         var v = this.getValue();
35556         if(String(v) !== String(this.startValue)){
35557             this.fireEvent('change', this, v, this.startValue);
35558         }
35559         this.fireEvent("blur", this);
35560     },
35561
35562     /**
35563      * Returns whether or not the field value is currently valid
35564      * @param {Boolean} preventMark True to disable marking the field invalid
35565      * @return {Boolean} True if the value is valid, else false
35566      */
35567     isValid : function(preventMark){
35568         if(this.disabled){
35569             return true;
35570         }
35571         var restore = this.preventMark;
35572         this.preventMark = preventMark === true;
35573         var v = this.validateValue(this.processValue(this.getRawValue()));
35574         this.preventMark = restore;
35575         return v;
35576     },
35577
35578     /**
35579      * Validates the field value
35580      * @return {Boolean} True if the value is valid, else false
35581      */
35582     validate : function(){
35583         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35584             this.clearInvalid();
35585             return true;
35586         }
35587         return false;
35588     },
35589
35590     processValue : function(value){
35591         return value;
35592     },
35593
35594     // private
35595     // Subclasses should provide the validation implementation by overriding this
35596     validateValue : function(value){
35597         return true;
35598     },
35599
35600     /**
35601      * Mark this field as invalid
35602      * @param {String} msg The validation message
35603      */
35604     markInvalid : function(msg){
35605         if(!this.rendered || this.preventMark){ // not rendered
35606             return;
35607         }
35608         this.el.addClass(this.invalidClass);
35609         msg = msg || this.invalidText;
35610         switch(this.msgTarget){
35611             case 'qtip':
35612                 this.el.dom.qtip = msg;
35613                 this.el.dom.qclass = 'x-form-invalid-tip';
35614                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35615                     Roo.QuickTips.enable();
35616                 }
35617                 break;
35618             case 'title':
35619                 this.el.dom.title = msg;
35620                 break;
35621             case 'under':
35622                 if(!this.errorEl){
35623                     var elp = this.el.findParent('.x-form-element', 5, true);
35624                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35625                     this.errorEl.setWidth(elp.getWidth(true)-20);
35626                 }
35627                 this.errorEl.update(msg);
35628                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35629                 break;
35630             case 'side':
35631                 if(!this.errorIcon){
35632                     var elp = this.el.findParent('.x-form-element', 5, true);
35633                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35634                 }
35635                 this.alignErrorIcon();
35636                 this.errorIcon.dom.qtip = msg;
35637                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35638                 this.errorIcon.show();
35639                 this.on('resize', this.alignErrorIcon, this);
35640                 break;
35641             default:
35642                 var t = Roo.getDom(this.msgTarget);
35643                 t.innerHTML = msg;
35644                 t.style.display = this.msgDisplay;
35645                 break;
35646         }
35647         this.fireEvent('invalid', this, msg);
35648     },
35649
35650     // private
35651     alignErrorIcon : function(){
35652         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35653     },
35654
35655     /**
35656      * Clear any invalid styles/messages for this field
35657      */
35658     clearInvalid : function(){
35659         if(!this.rendered || this.preventMark){ // not rendered
35660             return;
35661         }
35662         this.el.removeClass(this.invalidClass);
35663         switch(this.msgTarget){
35664             case 'qtip':
35665                 this.el.dom.qtip = '';
35666                 break;
35667             case 'title':
35668                 this.el.dom.title = '';
35669                 break;
35670             case 'under':
35671                 if(this.errorEl){
35672                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35673                 }
35674                 break;
35675             case 'side':
35676                 if(this.errorIcon){
35677                     this.errorIcon.dom.qtip = '';
35678                     this.errorIcon.hide();
35679                     this.un('resize', this.alignErrorIcon, this);
35680                 }
35681                 break;
35682             default:
35683                 var t = Roo.getDom(this.msgTarget);
35684                 t.innerHTML = '';
35685                 t.style.display = 'none';
35686                 break;
35687         }
35688         this.fireEvent('valid', this);
35689     },
35690
35691     /**
35692      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35693      * @return {Mixed} value The field value
35694      */
35695     getRawValue : function(){
35696         var v = this.el.getValue();
35697         if(v === this.emptyText){
35698             v = '';
35699         }
35700         return v;
35701     },
35702
35703     /**
35704      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35705      * @return {Mixed} value The field value
35706      */
35707     getValue : function(){
35708         var v = this.el.getValue();
35709         if(v === this.emptyText || v === undefined){
35710             v = '';
35711         }
35712         return v;
35713     },
35714
35715     /**
35716      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35717      * @param {Mixed} value The value to set
35718      */
35719     setRawValue : function(v){
35720         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35721     },
35722
35723     /**
35724      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35725      * @param {Mixed} value The value to set
35726      */
35727     setValue : function(v){
35728         this.value = v;
35729         if(this.rendered){
35730             this.el.dom.value = (v === null || v === undefined ? '' : v);
35731             this.validate();
35732         }
35733     },
35734
35735     adjustSize : function(w, h){
35736         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35737         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35738         return s;
35739     },
35740
35741     adjustWidth : function(tag, w){
35742         tag = tag.toLowerCase();
35743         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35744             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35745                 if(tag == 'input'){
35746                     return w + 2;
35747                 }
35748                 if(tag = 'textarea'){
35749                     return w-2;
35750                 }
35751             }else if(Roo.isOpera){
35752                 if(tag == 'input'){
35753                     return w + 2;
35754                 }
35755                 if(tag = 'textarea'){
35756                     return w-2;
35757                 }
35758             }
35759         }
35760         return w;
35761     }
35762 });
35763
35764
35765 // anything other than normal should be considered experimental
35766 Roo.form.Field.msgFx = {
35767     normal : {
35768         show: function(msgEl, f){
35769             msgEl.setDisplayed('block');
35770         },
35771
35772         hide : function(msgEl, f){
35773             msgEl.setDisplayed(false).update('');
35774         }
35775     },
35776
35777     slide : {
35778         show: function(msgEl, f){
35779             msgEl.slideIn('t', {stopFx:true});
35780         },
35781
35782         hide : function(msgEl, f){
35783             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35784         }
35785     },
35786
35787     slideRight : {
35788         show: function(msgEl, f){
35789             msgEl.fixDisplay();
35790             msgEl.alignTo(f.el, 'tl-tr');
35791             msgEl.slideIn('l', {stopFx:true});
35792         },
35793
35794         hide : function(msgEl, f){
35795             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35796         }
35797     }
35798 };/*
35799  * Based on:
35800  * Ext JS Library 1.1.1
35801  * Copyright(c) 2006-2007, Ext JS, LLC.
35802  *
35803  * Originally Released Under LGPL - original licence link has changed is not relivant.
35804  *
35805  * Fork - LGPL
35806  * <script type="text/javascript">
35807  */
35808  
35809
35810 /**
35811  * @class Roo.form.TextField
35812  * @extends Roo.form.Field
35813  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35814  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35815  * @constructor
35816  * Creates a new TextField
35817  * @param {Object} config Configuration options
35818  */
35819 Roo.form.TextField = function(config){
35820     Roo.form.TextField.superclass.constructor.call(this, config);
35821     this.addEvents({
35822         /**
35823          * @event autosize
35824          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35825          * according to the default logic, but this event provides a hook for the developer to apply additional
35826          * logic at runtime to resize the field if needed.
35827              * @param {Roo.form.Field} this This text field
35828              * @param {Number} width The new field width
35829              */
35830         autosize : true
35831     });
35832 };
35833
35834 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35835     /**
35836      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35837      */
35838     grow : false,
35839     /**
35840      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35841      */
35842     growMin : 30,
35843     /**
35844      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35845      */
35846     growMax : 800,
35847     /**
35848      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35849      */
35850     vtype : null,
35851     /**
35852      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35853      */
35854     maskRe : null,
35855     /**
35856      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35857      */
35858     disableKeyFilter : false,
35859     /**
35860      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35861      */
35862     allowBlank : true,
35863     /**
35864      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35865      */
35866     minLength : 0,
35867     /**
35868      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35869      */
35870     maxLength : Number.MAX_VALUE,
35871     /**
35872      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35873      */
35874     minLengthText : "The minimum length for this field is {0}",
35875     /**
35876      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35877      */
35878     maxLengthText : "The maximum length for this field is {0}",
35879     /**
35880      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35881      */
35882     selectOnFocus : false,
35883     /**
35884      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35885      */
35886     blankText : "This field is required",
35887     /**
35888      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35889      * If available, this function will be called only after the basic validators all return true, and will be passed the
35890      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35891      */
35892     validator : null,
35893     /**
35894      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35895      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35896      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35897      */
35898     regex : null,
35899     /**
35900      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35901      */
35902     regexText : "",
35903     /**
35904      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35905      */
35906     emptyText : null,
35907     /**
35908      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35909      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35910      */
35911     emptyClass : 'x-form-empty-field',
35912
35913     // private
35914     initEvents : function(){
35915         Roo.form.TextField.superclass.initEvents.call(this);
35916         if(this.validationEvent == 'keyup'){
35917             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35918             this.el.on('keyup', this.filterValidation, this);
35919         }
35920         else if(this.validationEvent !== false){
35921             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35922         }
35923         if(this.selectOnFocus || this.emptyText){
35924             this.on("focus", this.preFocus, this);
35925             if(this.emptyText){
35926                 this.on('blur', this.postBlur, this);
35927                 this.applyEmptyText();
35928             }
35929         }
35930         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
35931             this.el.on("keypress", this.filterKeys, this);
35932         }
35933         if(this.grow){
35934             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
35935             this.el.on("click", this.autoSize,  this);
35936         }
35937     },
35938
35939     processValue : function(value){
35940         if(this.stripCharsRe){
35941             var newValue = value.replace(this.stripCharsRe, '');
35942             if(newValue !== value){
35943                 this.setRawValue(newValue);
35944                 return newValue;
35945             }
35946         }
35947         return value;
35948     },
35949
35950     filterValidation : function(e){
35951         if(!e.isNavKeyPress()){
35952             this.validationTask.delay(this.validationDelay);
35953         }
35954     },
35955
35956     // private
35957     onKeyUp : function(e){
35958         if(!e.isNavKeyPress()){
35959             this.autoSize();
35960         }
35961     },
35962
35963     /**
35964      * Resets the current field value to the originally-loaded value and clears any validation messages.
35965      * Also adds emptyText and emptyClass if the original value was blank.
35966      */
35967     reset : function(){
35968         Roo.form.TextField.superclass.reset.call(this);
35969         this.applyEmptyText();
35970     },
35971
35972     applyEmptyText : function(){
35973         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
35974             this.setRawValue(this.emptyText);
35975             this.el.addClass(this.emptyClass);
35976         }
35977     },
35978
35979     // private
35980     preFocus : function(){
35981         if(this.emptyText){
35982             if(this.el.dom.value == this.emptyText){
35983                 this.setRawValue('');
35984             }
35985             this.el.removeClass(this.emptyClass);
35986         }
35987         if(this.selectOnFocus){
35988             this.el.dom.select();
35989         }
35990     },
35991
35992     // private
35993     postBlur : function(){
35994         this.applyEmptyText();
35995     },
35996
35997     // private
35998     filterKeys : function(e){
35999         var k = e.getKey();
36000         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36001             return;
36002         }
36003         var c = e.getCharCode(), cc = String.fromCharCode(c);
36004         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36005             return;
36006         }
36007         if(!this.maskRe.test(cc)){
36008             e.stopEvent();
36009         }
36010     },
36011
36012     setValue : function(v){
36013         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36014             this.el.removeClass(this.emptyClass);
36015         }
36016         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36017         this.applyEmptyText();
36018         this.autoSize();
36019     },
36020
36021     /**
36022      * Validates a value according to the field's validation rules and marks the field as invalid
36023      * if the validation fails
36024      * @param {Mixed} value The value to validate
36025      * @return {Boolean} True if the value is valid, else false
36026      */
36027     validateValue : function(value){
36028         if(value.length < 1 || value === this.emptyText){ // if it's blank
36029              if(this.allowBlank){
36030                 this.clearInvalid();
36031                 return true;
36032              }else{
36033                 this.markInvalid(this.blankText);
36034                 return false;
36035              }
36036         }
36037         if(value.length < this.minLength){
36038             this.markInvalid(String.format(this.minLengthText, this.minLength));
36039             return false;
36040         }
36041         if(value.length > this.maxLength){
36042             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36043             return false;
36044         }
36045         if(this.vtype){
36046             var vt = Roo.form.VTypes;
36047             if(!vt[this.vtype](value, this)){
36048                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36049                 return false;
36050             }
36051         }
36052         if(typeof this.validator == "function"){
36053             var msg = this.validator(value);
36054             if(msg !== true){
36055                 this.markInvalid(msg);
36056                 return false;
36057             }
36058         }
36059         if(this.regex && !this.regex.test(value)){
36060             this.markInvalid(this.regexText);
36061             return false;
36062         }
36063         return true;
36064     },
36065
36066     /**
36067      * Selects text in this field
36068      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36069      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36070      */
36071     selectText : function(start, end){
36072         var v = this.getRawValue();
36073         if(v.length > 0){
36074             start = start === undefined ? 0 : start;
36075             end = end === undefined ? v.length : end;
36076             var d = this.el.dom;
36077             if(d.setSelectionRange){
36078                 d.setSelectionRange(start, end);
36079             }else if(d.createTextRange){
36080                 var range = d.createTextRange();
36081                 range.moveStart("character", start);
36082                 range.moveEnd("character", v.length-end);
36083                 range.select();
36084             }
36085         }
36086     },
36087
36088     /**
36089      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36090      * This only takes effect if grow = true, and fires the autosize event.
36091      */
36092     autoSize : function(){
36093         if(!this.grow || !this.rendered){
36094             return;
36095         }
36096         if(!this.metrics){
36097             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36098         }
36099         var el = this.el;
36100         var v = el.dom.value;
36101         var d = document.createElement('div');
36102         d.appendChild(document.createTextNode(v));
36103         v = d.innerHTML;
36104         d = null;
36105         v += "&#160;";
36106         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36107         this.el.setWidth(w);
36108         this.fireEvent("autosize", this, w);
36109     }
36110 });/*
36111  * Based on:
36112  * Ext JS Library 1.1.1
36113  * Copyright(c) 2006-2007, Ext JS, LLC.
36114  *
36115  * Originally Released Under LGPL - original licence link has changed is not relivant.
36116  *
36117  * Fork - LGPL
36118  * <script type="text/javascript">
36119  */
36120  
36121 /**
36122  * @class Roo.form.Hidden
36123  * @extends Roo.form.TextField
36124  * Simple Hidden element used on forms 
36125  * 
36126  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36127  * 
36128  * @constructor
36129  * Creates a new Hidden form element.
36130  * @param {Object} config Configuration options
36131  */
36132
36133
36134
36135 // easy hidden field...
36136 Roo.form.Hidden = function(config){
36137     Roo.form.Hidden.superclass.constructor.call(this, config);
36138 };
36139   
36140 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36141     fieldLabel:      '',
36142     inputType:      'hidden',
36143     width:          50,
36144     allowBlank:     true,
36145     labelSeparator: '',
36146     hidden:         true,
36147     itemCls :       'x-form-item-display-none'
36148
36149
36150 });
36151
36152
36153 /*
36154  * Based on:
36155  * Ext JS Library 1.1.1
36156  * Copyright(c) 2006-2007, Ext JS, LLC.
36157  *
36158  * Originally Released Under LGPL - original licence link has changed is not relivant.
36159  *
36160  * Fork - LGPL
36161  * <script type="text/javascript">
36162  */
36163  
36164 /**
36165  * @class Roo.form.TriggerField
36166  * @extends Roo.form.TextField
36167  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36168  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36169  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36170  * for which you can provide a custom implementation.  For example:
36171  * <pre><code>
36172 var trigger = new Roo.form.TriggerField();
36173 trigger.onTriggerClick = myTriggerFn;
36174 trigger.applyTo('my-field');
36175 </code></pre>
36176  *
36177  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36178  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36179  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36180  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36181  * @constructor
36182  * Create a new TriggerField.
36183  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36184  * to the base TextField)
36185  */
36186 Roo.form.TriggerField = function(config){
36187     this.mimicing = false;
36188     Roo.form.TriggerField.superclass.constructor.call(this, config);
36189 };
36190
36191 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36192     /**
36193      * @cfg {String} triggerClass A CSS class to apply to the trigger
36194      */
36195     /**
36196      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36197      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36198      */
36199     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36200     /**
36201      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36202      */
36203     hideTrigger:false,
36204
36205     /** @cfg {Boolean} grow @hide */
36206     /** @cfg {Number} growMin @hide */
36207     /** @cfg {Number} growMax @hide */
36208
36209     /**
36210      * @hide 
36211      * @method
36212      */
36213     autoSize: Roo.emptyFn,
36214     // private
36215     monitorTab : true,
36216     // private
36217     deferHeight : true,
36218
36219     
36220     actionMode : 'wrap',
36221     // private
36222     onResize : function(w, h){
36223         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36224         if(typeof w == 'number'){
36225             var x = w - this.trigger.getWidth();
36226             this.el.setWidth(this.adjustWidth('input', x));
36227             this.trigger.setStyle('left', x+'px');
36228         }
36229     },
36230
36231     // private
36232     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36233
36234     // private
36235     getResizeEl : function(){
36236         return this.wrap;
36237     },
36238
36239     // private
36240     getPositionEl : function(){
36241         return this.wrap;
36242     },
36243
36244     // private
36245     alignErrorIcon : function(){
36246         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36247     },
36248
36249     // private
36250     onRender : function(ct, position){
36251         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36252         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36253         this.trigger = this.wrap.createChild(this.triggerConfig ||
36254                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36255         if(this.hideTrigger){
36256             this.trigger.setDisplayed(false);
36257         }
36258         this.initTrigger();
36259         if(!this.width){
36260             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36261         }
36262     },
36263
36264     // private
36265     initTrigger : function(){
36266         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36267         this.trigger.addClassOnOver('x-form-trigger-over');
36268         this.trigger.addClassOnClick('x-form-trigger-click');
36269     },
36270
36271     // private
36272     onDestroy : function(){
36273         if(this.trigger){
36274             this.trigger.removeAllListeners();
36275             this.trigger.remove();
36276         }
36277         if(this.wrap){
36278             this.wrap.remove();
36279         }
36280         Roo.form.TriggerField.superclass.onDestroy.call(this);
36281     },
36282
36283     // private
36284     onFocus : function(){
36285         Roo.form.TriggerField.superclass.onFocus.call(this);
36286         if(!this.mimicing){
36287             this.wrap.addClass('x-trigger-wrap-focus');
36288             this.mimicing = true;
36289             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36290             if(this.monitorTab){
36291                 this.el.on("keydown", this.checkTab, this);
36292             }
36293         }
36294     },
36295
36296     // private
36297     checkTab : function(e){
36298         if(e.getKey() == e.TAB){
36299             this.triggerBlur();
36300         }
36301     },
36302
36303     // private
36304     onBlur : function(){
36305         // do nothing
36306     },
36307
36308     // private
36309     mimicBlur : function(e, t){
36310         if(!this.wrap.contains(t) && this.validateBlur()){
36311             this.triggerBlur();
36312         }
36313     },
36314
36315     // private
36316     triggerBlur : function(){
36317         this.mimicing = false;
36318         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36319         if(this.monitorTab){
36320             this.el.un("keydown", this.checkTab, this);
36321         }
36322         this.wrap.removeClass('x-trigger-wrap-focus');
36323         Roo.form.TriggerField.superclass.onBlur.call(this);
36324     },
36325
36326     // private
36327     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36328     validateBlur : function(e, t){
36329         return true;
36330     },
36331
36332     // private
36333     onDisable : function(){
36334         Roo.form.TriggerField.superclass.onDisable.call(this);
36335         if(this.wrap){
36336             this.wrap.addClass('x-item-disabled');
36337         }
36338     },
36339
36340     // private
36341     onEnable : function(){
36342         Roo.form.TriggerField.superclass.onEnable.call(this);
36343         if(this.wrap){
36344             this.wrap.removeClass('x-item-disabled');
36345         }
36346     },
36347
36348     // private
36349     onShow : function(){
36350         var ae = this.getActionEl();
36351         
36352         if(ae){
36353             ae.dom.style.display = '';
36354             ae.dom.style.visibility = 'visible';
36355         }
36356     },
36357
36358     // private
36359     
36360     onHide : function(){
36361         var ae = this.getActionEl();
36362         ae.dom.style.display = 'none';
36363     },
36364
36365     /**
36366      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36367      * by an implementing function.
36368      * @method
36369      * @param {EventObject} e
36370      */
36371     onTriggerClick : Roo.emptyFn
36372 });
36373
36374 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36375 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36376 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36377 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36378     initComponent : function(){
36379         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36380
36381         this.triggerConfig = {
36382             tag:'span', cls:'x-form-twin-triggers', cn:[
36383             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36384             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36385         ]};
36386     },
36387
36388     getTrigger : function(index){
36389         return this.triggers[index];
36390     },
36391
36392     initTrigger : function(){
36393         var ts = this.trigger.select('.x-form-trigger', true);
36394         this.wrap.setStyle('overflow', 'hidden');
36395         var triggerField = this;
36396         ts.each(function(t, all, index){
36397             t.hide = function(){
36398                 var w = triggerField.wrap.getWidth();
36399                 this.dom.style.display = 'none';
36400                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36401             };
36402             t.show = function(){
36403                 var w = triggerField.wrap.getWidth();
36404                 this.dom.style.display = '';
36405                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36406             };
36407             var triggerIndex = 'Trigger'+(index+1);
36408
36409             if(this['hide'+triggerIndex]){
36410                 t.dom.style.display = 'none';
36411             }
36412             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36413             t.addClassOnOver('x-form-trigger-over');
36414             t.addClassOnClick('x-form-trigger-click');
36415         }, this);
36416         this.triggers = ts.elements;
36417     },
36418
36419     onTrigger1Click : Roo.emptyFn,
36420     onTrigger2Click : Roo.emptyFn
36421 });/*
36422  * Based on:
36423  * Ext JS Library 1.1.1
36424  * Copyright(c) 2006-2007, Ext JS, LLC.
36425  *
36426  * Originally Released Under LGPL - original licence link has changed is not relivant.
36427  *
36428  * Fork - LGPL
36429  * <script type="text/javascript">
36430  */
36431  
36432 /**
36433  * @class Roo.form.TextArea
36434  * @extends Roo.form.TextField
36435  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36436  * support for auto-sizing.
36437  * @constructor
36438  * Creates a new TextArea
36439  * @param {Object} config Configuration options
36440  */
36441 Roo.form.TextArea = function(config){
36442     Roo.form.TextArea.superclass.constructor.call(this, config);
36443     // these are provided exchanges for backwards compat
36444     // minHeight/maxHeight were replaced by growMin/growMax to be
36445     // compatible with TextField growing config values
36446     if(this.minHeight !== undefined){
36447         this.growMin = this.minHeight;
36448     }
36449     if(this.maxHeight !== undefined){
36450         this.growMax = this.maxHeight;
36451     }
36452 };
36453
36454 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36455     /**
36456      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36457      */
36458     growMin : 60,
36459     /**
36460      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36461      */
36462     growMax: 1000,
36463     /**
36464      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36465      * in the field (equivalent to setting overflow: hidden, defaults to false)
36466      */
36467     preventScrollbars: false,
36468     /**
36469      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36470      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36471      */
36472
36473     // private
36474     onRender : function(ct, position){
36475         if(!this.el){
36476             this.defaultAutoCreate = {
36477                 tag: "textarea",
36478                 style:"width:300px;height:60px;",
36479                 autocomplete: "off"
36480             };
36481         }
36482         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36483         if(this.grow){
36484             this.textSizeEl = Roo.DomHelper.append(document.body, {
36485                 tag: "pre", cls: "x-form-grow-sizer"
36486             });
36487             if(this.preventScrollbars){
36488                 this.el.setStyle("overflow", "hidden");
36489             }
36490             this.el.setHeight(this.growMin);
36491         }
36492     },
36493
36494     onDestroy : function(){
36495         if(this.textSizeEl){
36496             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36497         }
36498         Roo.form.TextArea.superclass.onDestroy.call(this);
36499     },
36500
36501     // private
36502     onKeyUp : function(e){
36503         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36504             this.autoSize();
36505         }
36506     },
36507
36508     /**
36509      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36510      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36511      */
36512     autoSize : function(){
36513         if(!this.grow || !this.textSizeEl){
36514             return;
36515         }
36516         var el = this.el;
36517         var v = el.dom.value;
36518         var ts = this.textSizeEl;
36519
36520         ts.innerHTML = '';
36521         ts.appendChild(document.createTextNode(v));
36522         v = ts.innerHTML;
36523
36524         Roo.fly(ts).setWidth(this.el.getWidth());
36525         if(v.length < 1){
36526             v = "&#160;&#160;";
36527         }else{
36528             if(Roo.isIE){
36529                 v = v.replace(/\n/g, '<p>&#160;</p>');
36530             }
36531             v += "&#160;\n&#160;";
36532         }
36533         ts.innerHTML = v;
36534         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36535         if(h != this.lastHeight){
36536             this.lastHeight = h;
36537             this.el.setHeight(h);
36538             this.fireEvent("autosize", this, h);
36539         }
36540     }
36541 });/*
36542  * Based on:
36543  * Ext JS Library 1.1.1
36544  * Copyright(c) 2006-2007, Ext JS, LLC.
36545  *
36546  * Originally Released Under LGPL - original licence link has changed is not relivant.
36547  *
36548  * Fork - LGPL
36549  * <script type="text/javascript">
36550  */
36551  
36552
36553 /**
36554  * @class Roo.form.NumberField
36555  * @extends Roo.form.TextField
36556  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36557  * @constructor
36558  * Creates a new NumberField
36559  * @param {Object} config Configuration options
36560  */
36561 Roo.form.NumberField = function(config){
36562     Roo.form.NumberField.superclass.constructor.call(this, config);
36563 };
36564
36565 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36566     /**
36567      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36568      */
36569     fieldClass: "x-form-field x-form-num-field",
36570     /**
36571      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36572      */
36573     allowDecimals : true,
36574     /**
36575      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36576      */
36577     decimalSeparator : ".",
36578     /**
36579      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36580      */
36581     decimalPrecision : 2,
36582     /**
36583      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36584      */
36585     allowNegative : true,
36586     /**
36587      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36588      */
36589     minValue : Number.NEGATIVE_INFINITY,
36590     /**
36591      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36592      */
36593     maxValue : Number.MAX_VALUE,
36594     /**
36595      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36596      */
36597     minText : "The minimum value for this field is {0}",
36598     /**
36599      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36600      */
36601     maxText : "The maximum value for this field is {0}",
36602     /**
36603      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36604      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36605      */
36606     nanText : "{0} is not a valid number",
36607
36608     // private
36609     initEvents : function(){
36610         Roo.form.NumberField.superclass.initEvents.call(this);
36611         var allowed = "0123456789";
36612         if(this.allowDecimals){
36613             allowed += this.decimalSeparator;
36614         }
36615         if(this.allowNegative){
36616             allowed += "-";
36617         }
36618         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36619         var keyPress = function(e){
36620             var k = e.getKey();
36621             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36622                 return;
36623             }
36624             var c = e.getCharCode();
36625             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36626                 e.stopEvent();
36627             }
36628         };
36629         this.el.on("keypress", keyPress, this);
36630     },
36631
36632     // private
36633     validateValue : function(value){
36634         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36635             return false;
36636         }
36637         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36638              return true;
36639         }
36640         var num = this.parseValue(value);
36641         if(isNaN(num)){
36642             this.markInvalid(String.format(this.nanText, value));
36643             return false;
36644         }
36645         if(num < this.minValue){
36646             this.markInvalid(String.format(this.minText, this.minValue));
36647             return false;
36648         }
36649         if(num > this.maxValue){
36650             this.markInvalid(String.format(this.maxText, this.maxValue));
36651             return false;
36652         }
36653         return true;
36654     },
36655
36656     getValue : function(){
36657         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36658     },
36659
36660     // private
36661     parseValue : function(value){
36662         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36663         return isNaN(value) ? '' : value;
36664     },
36665
36666     // private
36667     fixPrecision : function(value){
36668         var nan = isNaN(value);
36669         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36670             return nan ? '' : value;
36671         }
36672         return parseFloat(value).toFixed(this.decimalPrecision);
36673     },
36674
36675     setValue : function(v){
36676         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36677     },
36678
36679     // private
36680     decimalPrecisionFcn : function(v){
36681         return Math.floor(v);
36682     },
36683
36684     beforeBlur : function(){
36685         var v = this.parseValue(this.getRawValue());
36686         if(v){
36687             this.setValue(this.fixPrecision(v));
36688         }
36689     }
36690 });/*
36691  * Based on:
36692  * Ext JS Library 1.1.1
36693  * Copyright(c) 2006-2007, Ext JS, LLC.
36694  *
36695  * Originally Released Under LGPL - original licence link has changed is not relivant.
36696  *
36697  * Fork - LGPL
36698  * <script type="text/javascript">
36699  */
36700  
36701 /**
36702  * @class Roo.form.DateField
36703  * @extends Roo.form.TriggerField
36704  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36705 * @constructor
36706 * Create a new DateField
36707 * @param {Object} config
36708  */
36709 Roo.form.DateField = function(config){
36710     Roo.form.DateField.superclass.constructor.call(this, config);
36711     
36712       this.addEvents({
36713          
36714         /**
36715          * @event select
36716          * Fires when a date is selected
36717              * @param {Roo.form.DateField} combo This combo box
36718              * @param {Date} date The date selected
36719              */
36720         'select' : true
36721          
36722     });
36723     
36724     
36725     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36726     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36727     this.ddMatch = null;
36728     if(this.disabledDates){
36729         var dd = this.disabledDates;
36730         var re = "(?:";
36731         for(var i = 0; i < dd.length; i++){
36732             re += dd[i];
36733             if(i != dd.length-1) re += "|";
36734         }
36735         this.ddMatch = new RegExp(re + ")");
36736     }
36737 };
36738
36739 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36740     /**
36741      * @cfg {String} format
36742      * The default date format string which can be overriden for localization support.  The format must be
36743      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36744      */
36745     format : "m/d/y",
36746     /**
36747      * @cfg {String} altFormats
36748      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36749      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36750      */
36751     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36752     /**
36753      * @cfg {Array} disabledDays
36754      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36755      */
36756     disabledDays : null,
36757     /**
36758      * @cfg {String} disabledDaysText
36759      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36760      */
36761     disabledDaysText : "Disabled",
36762     /**
36763      * @cfg {Array} disabledDates
36764      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36765      * expression so they are very powerful. Some examples:
36766      * <ul>
36767      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36768      * <li>["03/08", "09/16"] would disable those days for every year</li>
36769      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36770      * <li>["03/../2006"] would disable every day in March 2006</li>
36771      * <li>["^03"] would disable every day in every March</li>
36772      * </ul>
36773      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36774      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36775      */
36776     disabledDates : null,
36777     /**
36778      * @cfg {String} disabledDatesText
36779      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36780      */
36781     disabledDatesText : "Disabled",
36782     /**
36783      * @cfg {Date/String} minValue
36784      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36785      * valid format (defaults to null).
36786      */
36787     minValue : null,
36788     /**
36789      * @cfg {Date/String} maxValue
36790      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36791      * valid format (defaults to null).
36792      */
36793     maxValue : null,
36794     /**
36795      * @cfg {String} minText
36796      * The error text to display when the date in the cell is before minValue (defaults to
36797      * 'The date in this field must be after {minValue}').
36798      */
36799     minText : "The date in this field must be equal to or after {0}",
36800     /**
36801      * @cfg {String} maxText
36802      * The error text to display when the date in the cell is after maxValue (defaults to
36803      * 'The date in this field must be before {maxValue}').
36804      */
36805     maxText : "The date in this field must be equal to or before {0}",
36806     /**
36807      * @cfg {String} invalidText
36808      * The error text to display when the date in the field is invalid (defaults to
36809      * '{value} is not a valid date - it must be in the format {format}').
36810      */
36811     invalidText : "{0} is not a valid date - it must be in the format {1}",
36812     /**
36813      * @cfg {String} triggerClass
36814      * An additional CSS class used to style the trigger button.  The trigger will always get the
36815      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36816      * which displays a calendar icon).
36817      */
36818     triggerClass : 'x-form-date-trigger',
36819     
36820
36821     /**
36822      * @cfg {bool} useIso
36823      * if enabled, then the date field will use a hidden field to store the 
36824      * real value as iso formated date. default (false)
36825      */ 
36826     useIso : false,
36827     /**
36828      * @cfg {String/Object} autoCreate
36829      * A DomHelper element spec, or true for a default element spec (defaults to
36830      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36831      */ 
36832     // private
36833     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36834     
36835     // private
36836     hiddenField: false,
36837     
36838     onRender : function(ct, position)
36839     {
36840         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36841         if (this.useIso) {
36842             this.el.dom.removeAttribute('name'); 
36843             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36844                     'before', true);
36845             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36846             // prevent input submission
36847             this.hiddenName = this.name;
36848         }
36849             
36850             
36851     },
36852     
36853     // private
36854     validateValue : function(value)
36855     {
36856         value = this.formatDate(value);
36857         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36858             return false;
36859         }
36860         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36861              return true;
36862         }
36863         var svalue = value;
36864         value = this.parseDate(value);
36865         if(!value){
36866             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36867             return false;
36868         }
36869         var time = value.getTime();
36870         if(this.minValue && time < this.minValue.getTime()){
36871             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36872             return false;
36873         }
36874         if(this.maxValue && time > this.maxValue.getTime()){
36875             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36876             return false;
36877         }
36878         if(this.disabledDays){
36879             var day = value.getDay();
36880             for(var i = 0; i < this.disabledDays.length; i++) {
36881                 if(day === this.disabledDays[i]){
36882                     this.markInvalid(this.disabledDaysText);
36883                     return false;
36884                 }
36885             }
36886         }
36887         var fvalue = this.formatDate(value);
36888         if(this.ddMatch && this.ddMatch.test(fvalue)){
36889             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36890             return false;
36891         }
36892         return true;
36893     },
36894
36895     // private
36896     // Provides logic to override the default TriggerField.validateBlur which just returns true
36897     validateBlur : function(){
36898         return !this.menu || !this.menu.isVisible();
36899     },
36900
36901     /**
36902      * Returns the current date value of the date field.
36903      * @return {Date} The date value
36904      */
36905     getValue : function(){
36906         
36907         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36908     },
36909
36910     /**
36911      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36912      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36913      * (the default format used is "m/d/y").
36914      * <br />Usage:
36915      * <pre><code>
36916 //All of these calls set the same date value (May 4, 2006)
36917
36918 //Pass a date object:
36919 var dt = new Date('5/4/06');
36920 dateField.setValue(dt);
36921
36922 //Pass a date string (default format):
36923 dateField.setValue('5/4/06');
36924
36925 //Pass a date string (custom format):
36926 dateField.format = 'Y-m-d';
36927 dateField.setValue('2006-5-4');
36928 </code></pre>
36929      * @param {String/Date} date The date or valid date string
36930      */
36931     setValue : function(date){
36932         if (this.hiddenField) {
36933             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
36934         }
36935         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
36936     },
36937
36938     // private
36939     parseDate : function(value){
36940         if(!value || value instanceof Date){
36941             return value;
36942         }
36943         var v = Date.parseDate(value, this.format);
36944         if(!v && this.altFormats){
36945             if(!this.altFormatsArray){
36946                 this.altFormatsArray = this.altFormats.split("|");
36947             }
36948             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
36949                 v = Date.parseDate(value, this.altFormatsArray[i]);
36950             }
36951         }
36952         return v;
36953     },
36954
36955     // private
36956     formatDate : function(date, fmt){
36957         return (!date || !(date instanceof Date)) ?
36958                date : date.dateFormat(fmt || this.format);
36959     },
36960
36961     // private
36962     menuListeners : {
36963         select: function(m, d){
36964             this.setValue(d);
36965             this.fireEvent('select', this, d);
36966         },
36967         show : function(){ // retain focus styling
36968             this.onFocus();
36969         },
36970         hide : function(){
36971             this.focus.defer(10, this);
36972             var ml = this.menuListeners;
36973             this.menu.un("select", ml.select,  this);
36974             this.menu.un("show", ml.show,  this);
36975             this.menu.un("hide", ml.hide,  this);
36976         }
36977     },
36978
36979     // private
36980     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
36981     onTriggerClick : function(){
36982         if(this.disabled){
36983             return;
36984         }
36985         if(this.menu == null){
36986             this.menu = new Roo.menu.DateMenu();
36987         }
36988         Roo.apply(this.menu.picker,  {
36989             showClear: this.allowBlank,
36990             minDate : this.minValue,
36991             maxDate : this.maxValue,
36992             disabledDatesRE : this.ddMatch,
36993             disabledDatesText : this.disabledDatesText,
36994             disabledDays : this.disabledDays,
36995             disabledDaysText : this.disabledDaysText,
36996             format : this.format,
36997             minText : String.format(this.minText, this.formatDate(this.minValue)),
36998             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
36999         });
37000         this.menu.on(Roo.apply({}, this.menuListeners, {
37001             scope:this
37002         }));
37003         this.menu.picker.setValue(this.getValue() || new Date());
37004         this.menu.show(this.el, "tl-bl?");
37005     },
37006
37007     beforeBlur : function(){
37008         var v = this.parseDate(this.getRawValue());
37009         if(v){
37010             this.setValue(v);
37011         }
37012     }
37013
37014     /** @cfg {Boolean} grow @hide */
37015     /** @cfg {Number} growMin @hide */
37016     /** @cfg {Number} growMax @hide */
37017     /**
37018      * @hide
37019      * @method autoSize
37020      */
37021 });/*
37022  * Based on:
37023  * Ext JS Library 1.1.1
37024  * Copyright(c) 2006-2007, Ext JS, LLC.
37025  *
37026  * Originally Released Under LGPL - original licence link has changed is not relivant.
37027  *
37028  * Fork - LGPL
37029  * <script type="text/javascript">
37030  */
37031  
37032
37033 /**
37034  * @class Roo.form.ComboBox
37035  * @extends Roo.form.TriggerField
37036  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37037  * @constructor
37038  * Create a new ComboBox.
37039  * @param {Object} config Configuration options
37040  */
37041 Roo.form.ComboBox = function(config){
37042     Roo.form.ComboBox.superclass.constructor.call(this, config);
37043     this.addEvents({
37044         /**
37045          * @event expand
37046          * Fires when the dropdown list is expanded
37047              * @param {Roo.form.ComboBox} combo This combo box
37048              */
37049         'expand' : true,
37050         /**
37051          * @event collapse
37052          * Fires when the dropdown list is collapsed
37053              * @param {Roo.form.ComboBox} combo This combo box
37054              */
37055         'collapse' : true,
37056         /**
37057          * @event beforeselect
37058          * Fires before a list item is selected. Return false to cancel the selection.
37059              * @param {Roo.form.ComboBox} combo This combo box
37060              * @param {Roo.data.Record} record The data record returned from the underlying store
37061              * @param {Number} index The index of the selected item in the dropdown list
37062              */
37063         'beforeselect' : true,
37064         /**
37065          * @event select
37066          * Fires when a list item is selected
37067              * @param {Roo.form.ComboBox} combo This combo box
37068              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37069              * @param {Number} index The index of the selected item in the dropdown list
37070              */
37071         'select' : true,
37072         /**
37073          * @event beforequery
37074          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37075          * The event object passed has these properties:
37076              * @param {Roo.form.ComboBox} combo This combo box
37077              * @param {String} query The query
37078              * @param {Boolean} forceAll true to force "all" query
37079              * @param {Boolean} cancel true to cancel the query
37080              * @param {Object} e The query event object
37081              */
37082         'beforequery': true,
37083          /**
37084          * @event add
37085          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37086              * @param {Roo.form.ComboBox} combo This combo box
37087              */
37088         'add' : true,
37089         /**
37090          * @event edit
37091          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37092              * @param {Roo.form.ComboBox} combo This combo box
37093              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37094              */
37095         'edit' : true
37096         
37097         
37098     });
37099     if(this.transform){
37100         this.allowDomMove = false;
37101         var s = Roo.getDom(this.transform);
37102         if(!this.hiddenName){
37103             this.hiddenName = s.name;
37104         }
37105         if(!this.store){
37106             this.mode = 'local';
37107             var d = [], opts = s.options;
37108             for(var i = 0, len = opts.length;i < len; i++){
37109                 var o = opts[i];
37110                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37111                 if(o.selected) {
37112                     this.value = value;
37113                 }
37114                 d.push([value, o.text]);
37115             }
37116             this.store = new Roo.data.SimpleStore({
37117                 'id': 0,
37118                 fields: ['value', 'text'],
37119                 data : d
37120             });
37121             this.valueField = 'value';
37122             this.displayField = 'text';
37123         }
37124         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37125         if(!this.lazyRender){
37126             this.target = true;
37127             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37128             s.parentNode.removeChild(s); // remove it
37129             this.render(this.el.parentNode);
37130         }else{
37131             s.parentNode.removeChild(s); // remove it
37132         }
37133
37134     }
37135     if (this.store) {
37136         this.store = Roo.factory(this.store, Roo.data);
37137     }
37138     
37139     this.selectedIndex = -1;
37140     if(this.mode == 'local'){
37141         if(config.queryDelay === undefined){
37142             this.queryDelay = 10;
37143         }
37144         if(config.minChars === undefined){
37145             this.minChars = 0;
37146         }
37147     }
37148 };
37149
37150 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37151     /**
37152      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37153      */
37154     /**
37155      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37156      * rendering into an Roo.Editor, defaults to false)
37157      */
37158     /**
37159      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37160      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37161      */
37162     /**
37163      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37164      */
37165     /**
37166      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37167      * the dropdown list (defaults to undefined, with no header element)
37168      */
37169
37170      /**
37171      * @cfg {String/Roo.Template} tpl The template to use to render the output
37172      */
37173      
37174     // private
37175     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37176     /**
37177      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37178      */
37179     listWidth: undefined,
37180     /**
37181      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37182      * mode = 'remote' or 'text' if mode = 'local')
37183      */
37184     displayField: undefined,
37185     /**
37186      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37187      * mode = 'remote' or 'value' if mode = 'local'). 
37188      * Note: use of a valueField requires the user make a selection
37189      * in order for a value to be mapped.
37190      */
37191     valueField: undefined,
37192     /**
37193      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37194      * field's data value (defaults to the underlying DOM element's name)
37195      */
37196     hiddenName: undefined,
37197     /**
37198      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37199      */
37200     listClass: '',
37201     /**
37202      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37203      */
37204     selectedClass: 'x-combo-selected',
37205     /**
37206      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37207      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37208      * which displays a downward arrow icon).
37209      */
37210     triggerClass : 'x-form-arrow-trigger',
37211     /**
37212      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37213      */
37214     shadow:'sides',
37215     /**
37216      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37217      * anchor positions (defaults to 'tl-bl')
37218      */
37219     listAlign: 'tl-bl?',
37220     /**
37221      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37222      */
37223     maxHeight: 300,
37224     /**
37225      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37226      * query specified by the allQuery config option (defaults to 'query')
37227      */
37228     triggerAction: 'query',
37229     /**
37230      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37231      * (defaults to 4, does not apply if editable = false)
37232      */
37233     minChars : 4,
37234     /**
37235      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37236      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37237      */
37238     typeAhead: false,
37239     /**
37240      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37241      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37242      */
37243     queryDelay: 500,
37244     /**
37245      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37246      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37247      */
37248     pageSize: 0,
37249     /**
37250      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37251      * when editable = true (defaults to false)
37252      */
37253     selectOnFocus:false,
37254     /**
37255      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37256      */
37257     queryParam: 'query',
37258     /**
37259      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37260      * when mode = 'remote' (defaults to 'Loading...')
37261      */
37262     loadingText: 'Loading...',
37263     /**
37264      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37265      */
37266     resizable: false,
37267     /**
37268      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37269      */
37270     handleHeight : 8,
37271     /**
37272      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37273      * traditional select (defaults to true)
37274      */
37275     editable: true,
37276     /**
37277      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37278      */
37279     allQuery: '',
37280     /**
37281      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37282      */
37283     mode: 'remote',
37284     /**
37285      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37286      * listWidth has a higher value)
37287      */
37288     minListWidth : 70,
37289     /**
37290      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37291      * allow the user to set arbitrary text into the field (defaults to false)
37292      */
37293     forceSelection:false,
37294     /**
37295      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37296      * if typeAhead = true (defaults to 250)
37297      */
37298     typeAheadDelay : 250,
37299     /**
37300      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37301      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37302      */
37303     valueNotFoundText : undefined,
37304     /**
37305      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37306      */
37307     blockFocus : false,
37308     
37309     /**
37310      * @cfg {Boolean} disableClear Disable showing of clear button.
37311      */
37312     disableClear : false,
37313     /**
37314      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37315      */
37316     alwaysQuery : false,
37317     
37318     //private
37319     addicon : false,
37320     editicon: false,
37321     
37322     
37323     // private
37324     onRender : function(ct, position){
37325         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37326         if(this.hiddenName){
37327             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37328                     'before', true);
37329             this.hiddenField.value =
37330                 this.hiddenValue !== undefined ? this.hiddenValue :
37331                 this.value !== undefined ? this.value : '';
37332
37333             // prevent input submission
37334             this.el.dom.removeAttribute('name');
37335         }
37336         if(Roo.isGecko){
37337             this.el.dom.setAttribute('autocomplete', 'off');
37338         }
37339
37340         var cls = 'x-combo-list';
37341
37342         this.list = new Roo.Layer({
37343             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37344         });
37345
37346         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37347         this.list.setWidth(lw);
37348         this.list.swallowEvent('mousewheel');
37349         this.assetHeight = 0;
37350
37351         if(this.title){
37352             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37353             this.assetHeight += this.header.getHeight();
37354         }
37355
37356         this.innerList = this.list.createChild({cls:cls+'-inner'});
37357         this.innerList.on('mouseover', this.onViewOver, this);
37358         this.innerList.on('mousemove', this.onViewMove, this);
37359         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37360         
37361         if(this.allowBlank && !this.pageSize && !this.disableClear){
37362             this.footer = this.list.createChild({cls:cls+'-ft'});
37363             this.pageTb = new Roo.Toolbar(this.footer);
37364            
37365         }
37366         if(this.pageSize){
37367             this.footer = this.list.createChild({cls:cls+'-ft'});
37368             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37369                     {pageSize: this.pageSize});
37370             
37371         }
37372         
37373         if (this.pageTb && this.allowBlank && !this.disableClear) {
37374             var _this = this;
37375             this.pageTb.add(new Roo.Toolbar.Fill(), {
37376                 cls: 'x-btn-icon x-btn-clear',
37377                 text: '&#160;',
37378                 handler: function()
37379                 {
37380                     _this.collapse();
37381                     _this.clearValue();
37382                     _this.onSelect(false, -1);
37383                 }
37384             });
37385         }
37386         if (this.footer) {
37387             this.assetHeight += this.footer.getHeight();
37388         }
37389         
37390
37391         if(!this.tpl){
37392             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37393         }
37394
37395         this.view = new Roo.View(this.innerList, this.tpl, {
37396             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37397         });
37398
37399         this.view.on('click', this.onViewClick, this);
37400
37401         this.store.on('beforeload', this.onBeforeLoad, this);
37402         this.store.on('load', this.onLoad, this);
37403         this.store.on('loadexception', this.collapse, this);
37404
37405         if(this.resizable){
37406             this.resizer = new Roo.Resizable(this.list,  {
37407                pinned:true, handles:'se'
37408             });
37409             this.resizer.on('resize', function(r, w, h){
37410                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37411                 this.listWidth = w;
37412                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37413                 this.restrictHeight();
37414             }, this);
37415             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37416         }
37417         if(!this.editable){
37418             this.editable = true;
37419             this.setEditable(false);
37420         }  
37421         
37422         
37423         if (typeof(this.events.add.listeners) != 'undefined') {
37424             
37425             this.addicon = this.wrap.createChild(
37426                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37427        
37428             this.addicon.on('click', function(e) {
37429                 this.fireEvent('add', this);
37430             }, this);
37431         }
37432         if (typeof(this.events.edit.listeners) != 'undefined') {
37433             
37434             this.editicon = this.wrap.createChild(
37435                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37436             if (this.addicon) {
37437                 this.editicon.setStyle('margin-left', '40px');
37438             }
37439             this.editicon.on('click', function(e) {
37440                 
37441                 // we fire even  if inothing is selected..
37442                 this.fireEvent('edit', this, this.lastData );
37443                 
37444             }, this);
37445         }
37446         
37447         
37448         
37449     },
37450
37451     // private
37452     initEvents : function(){
37453         Roo.form.ComboBox.superclass.initEvents.call(this);
37454
37455         this.keyNav = new Roo.KeyNav(this.el, {
37456             "up" : function(e){
37457                 this.inKeyMode = true;
37458                 this.selectPrev();
37459             },
37460
37461             "down" : function(e){
37462                 if(!this.isExpanded()){
37463                     this.onTriggerClick();
37464                 }else{
37465                     this.inKeyMode = true;
37466                     this.selectNext();
37467                 }
37468             },
37469
37470             "enter" : function(e){
37471                 this.onViewClick();
37472                 //return true;
37473             },
37474
37475             "esc" : function(e){
37476                 this.collapse();
37477             },
37478
37479             "tab" : function(e){
37480                 this.onViewClick(false);
37481                 return true;
37482             },
37483
37484             scope : this,
37485
37486             doRelay : function(foo, bar, hname){
37487                 if(hname == 'down' || this.scope.isExpanded()){
37488                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37489                 }
37490                 return true;
37491             },
37492
37493             forceKeyDown: true
37494         });
37495         this.queryDelay = Math.max(this.queryDelay || 10,
37496                 this.mode == 'local' ? 10 : 250);
37497         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37498         if(this.typeAhead){
37499             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37500         }
37501         if(this.editable !== false){
37502             this.el.on("keyup", this.onKeyUp, this);
37503         }
37504         if(this.forceSelection){
37505             this.on('blur', this.doForce, this);
37506         }
37507     },
37508
37509     onDestroy : function(){
37510         if(this.view){
37511             this.view.setStore(null);
37512             this.view.el.removeAllListeners();
37513             this.view.el.remove();
37514             this.view.purgeListeners();
37515         }
37516         if(this.list){
37517             this.list.destroy();
37518         }
37519         if(this.store){
37520             this.store.un('beforeload', this.onBeforeLoad, this);
37521             this.store.un('load', this.onLoad, this);
37522             this.store.un('loadexception', this.collapse, this);
37523         }
37524         Roo.form.ComboBox.superclass.onDestroy.call(this);
37525     },
37526
37527     // private
37528     fireKey : function(e){
37529         if(e.isNavKeyPress() && !this.list.isVisible()){
37530             this.fireEvent("specialkey", this, e);
37531         }
37532     },
37533
37534     // private
37535     onResize: function(w, h){
37536         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37537         
37538         if(typeof w != 'number'){
37539             // we do not handle it!?!?
37540             return;
37541         }
37542         var tw = this.trigger.getWidth();
37543         tw += this.addicon ? this.addicon.getWidth() : 0;
37544         tw += this.editicon ? this.editicon.getWidth() : 0;
37545         var x = w - tw;
37546         this.el.setWidth( this.adjustWidth('input', x));
37547             
37548         this.trigger.setStyle('left', x+'px');
37549         
37550         if(this.list && this.listWidth === undefined){
37551             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37552             this.list.setWidth(lw);
37553             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37554         }
37555         
37556     
37557         
37558     },
37559
37560     /**
37561      * Allow or prevent the user from directly editing the field text.  If false is passed,
37562      * the user will only be able to select from the items defined in the dropdown list.  This method
37563      * is the runtime equivalent of setting the 'editable' config option at config time.
37564      * @param {Boolean} value True to allow the user to directly edit the field text
37565      */
37566     setEditable : function(value){
37567         if(value == this.editable){
37568             return;
37569         }
37570         this.editable = value;
37571         if(!value){
37572             this.el.dom.setAttribute('readOnly', true);
37573             this.el.on('mousedown', this.onTriggerClick,  this);
37574             this.el.addClass('x-combo-noedit');
37575         }else{
37576             this.el.dom.setAttribute('readOnly', false);
37577             this.el.un('mousedown', this.onTriggerClick,  this);
37578             this.el.removeClass('x-combo-noedit');
37579         }
37580     },
37581
37582     // private
37583     onBeforeLoad : function(){
37584         if(!this.hasFocus){
37585             return;
37586         }
37587         this.innerList.update(this.loadingText ?
37588                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37589         this.restrictHeight();
37590         this.selectedIndex = -1;
37591     },
37592
37593     // private
37594     onLoad : function(){
37595         if(!this.hasFocus){
37596             return;
37597         }
37598         if(this.store.getCount() > 0){
37599             this.expand();
37600             this.restrictHeight();
37601             if(this.lastQuery == this.allQuery){
37602                 if(this.editable){
37603                     this.el.dom.select();
37604                 }
37605                 if(!this.selectByValue(this.value, true)){
37606                     this.select(0, true);
37607                 }
37608             }else{
37609                 this.selectNext();
37610                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37611                     this.taTask.delay(this.typeAheadDelay);
37612                 }
37613             }
37614         }else{
37615             this.onEmptyResults();
37616         }
37617         //this.el.focus();
37618     },
37619
37620     // private
37621     onTypeAhead : function(){
37622         if(this.store.getCount() > 0){
37623             var r = this.store.getAt(0);
37624             var newValue = r.data[this.displayField];
37625             var len = newValue.length;
37626             var selStart = this.getRawValue().length;
37627             if(selStart != len){
37628                 this.setRawValue(newValue);
37629                 this.selectText(selStart, newValue.length);
37630             }
37631         }
37632     },
37633
37634     // private
37635     onSelect : function(record, index){
37636         if(this.fireEvent('beforeselect', this, record, index) !== false){
37637             this.setFromData(index > -1 ? record.data : false);
37638             this.collapse();
37639             this.fireEvent('select', this, record, index);
37640         }
37641     },
37642
37643     /**
37644      * Returns the currently selected field value or empty string if no value is set.
37645      * @return {String} value The selected value
37646      */
37647     getValue : function(){
37648         if(this.valueField){
37649             return typeof this.value != 'undefined' ? this.value : '';
37650         }else{
37651             return Roo.form.ComboBox.superclass.getValue.call(this);
37652         }
37653     },
37654
37655     /**
37656      * Clears any text/value currently set in the field
37657      */
37658     clearValue : function(){
37659         if(this.hiddenField){
37660             this.hiddenField.value = '';
37661         }
37662         this.value = '';
37663         this.setRawValue('');
37664         this.lastSelectionText = '';
37665         this.applyEmptyText();
37666     },
37667
37668     /**
37669      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37670      * will be displayed in the field.  If the value does not match the data value of an existing item,
37671      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37672      * Otherwise the field will be blank (although the value will still be set).
37673      * @param {String} value The value to match
37674      */
37675     setValue : function(v){
37676         var text = v;
37677         if(this.valueField){
37678             var r = this.findRecord(this.valueField, v);
37679             if(r){
37680                 text = r.data[this.displayField];
37681             }else if(this.valueNotFoundText !== undefined){
37682                 text = this.valueNotFoundText;
37683             }
37684         }
37685         this.lastSelectionText = text;
37686         if(this.hiddenField){
37687             this.hiddenField.value = v;
37688         }
37689         Roo.form.ComboBox.superclass.setValue.call(this, text);
37690         this.value = v;
37691     },
37692     /**
37693      * @property {Object} the last set data for the element
37694      */
37695     
37696     lastData : false,
37697     /**
37698      * Sets the value of the field based on a object which is related to the record format for the store.
37699      * @param {Object} value the value to set as. or false on reset?
37700      */
37701     setFromData : function(o){
37702         var dv = ''; // display value
37703         var vv = ''; // value value..
37704         this.lastData = o;
37705         if (this.displayField) {
37706             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37707         } else {
37708             // this is an error condition!!!
37709             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37710         }
37711         
37712         if(this.valueField){
37713             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37714         }
37715         if(this.hiddenField){
37716             this.hiddenField.value = vv;
37717             
37718             this.lastSelectionText = dv;
37719             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37720             this.value = vv;
37721             return;
37722         }
37723         // no hidden field.. - we store the value in 'value', but still display
37724         // display field!!!!
37725         this.lastSelectionText = dv;
37726         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37727         this.value = vv;
37728         
37729         
37730     },
37731     // private
37732     reset : function(){
37733         // overridden so that last data is reset..
37734         this.setValue(this.originalValue);
37735         this.clearInvalid();
37736         this.lastData = false;
37737     },
37738     // private
37739     findRecord : function(prop, value){
37740         var record;
37741         if(this.store.getCount() > 0){
37742             this.store.each(function(r){
37743                 if(r.data[prop] == value){
37744                     record = r;
37745                     return false;
37746                 }
37747             });
37748         }
37749         return record;
37750     },
37751
37752     // private
37753     onViewMove : function(e, t){
37754         this.inKeyMode = false;
37755     },
37756
37757     // private
37758     onViewOver : function(e, t){
37759         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37760             return;
37761         }
37762         var item = this.view.findItemFromChild(t);
37763         if(item){
37764             var index = this.view.indexOf(item);
37765             this.select(index, false);
37766         }
37767     },
37768
37769     // private
37770     onViewClick : function(doFocus){
37771         var index = this.view.getSelectedIndexes()[0];
37772         var r = this.store.getAt(index);
37773         if(r){
37774             this.onSelect(r, index);
37775         }
37776         if(doFocus !== false && !this.blockFocus){
37777             this.el.focus();
37778         }
37779     },
37780
37781     // private
37782     restrictHeight : function(){
37783         this.innerList.dom.style.height = '';
37784         var inner = this.innerList.dom;
37785         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37786         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37787         this.list.beginUpdate();
37788         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37789         this.list.alignTo(this.el, this.listAlign);
37790         this.list.endUpdate();
37791     },
37792
37793     // private
37794     onEmptyResults : function(){
37795         this.collapse();
37796     },
37797
37798     /**
37799      * Returns true if the dropdown list is expanded, else false.
37800      */
37801     isExpanded : function(){
37802         return this.list.isVisible();
37803     },
37804
37805     /**
37806      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37807      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37808      * @param {String} value The data value of the item to select
37809      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37810      * selected item if it is not currently in view (defaults to true)
37811      * @return {Boolean} True if the value matched an item in the list, else false
37812      */
37813     selectByValue : function(v, scrollIntoView){
37814         if(v !== undefined && v !== null){
37815             var r = this.findRecord(this.valueField || this.displayField, v);
37816             if(r){
37817                 this.select(this.store.indexOf(r), scrollIntoView);
37818                 return true;
37819             }
37820         }
37821         return false;
37822     },
37823
37824     /**
37825      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37826      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37827      * @param {Number} index The zero-based index of the list item to select
37828      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37829      * selected item if it is not currently in view (defaults to true)
37830      */
37831     select : function(index, scrollIntoView){
37832         this.selectedIndex = index;
37833         this.view.select(index);
37834         if(scrollIntoView !== false){
37835             var el = this.view.getNode(index);
37836             if(el){
37837                 this.innerList.scrollChildIntoView(el, false);
37838             }
37839         }
37840     },
37841
37842     // private
37843     selectNext : function(){
37844         var ct = this.store.getCount();
37845         if(ct > 0){
37846             if(this.selectedIndex == -1){
37847                 this.select(0);
37848             }else if(this.selectedIndex < ct-1){
37849                 this.select(this.selectedIndex+1);
37850             }
37851         }
37852     },
37853
37854     // private
37855     selectPrev : function(){
37856         var ct = this.store.getCount();
37857         if(ct > 0){
37858             if(this.selectedIndex == -1){
37859                 this.select(0);
37860             }else if(this.selectedIndex != 0){
37861                 this.select(this.selectedIndex-1);
37862             }
37863         }
37864     },
37865
37866     // private
37867     onKeyUp : function(e){
37868         if(this.editable !== false && !e.isSpecialKey()){
37869             this.lastKey = e.getKey();
37870             this.dqTask.delay(this.queryDelay);
37871         }
37872     },
37873
37874     // private
37875     validateBlur : function(){
37876         return !this.list || !this.list.isVisible();   
37877     },
37878
37879     // private
37880     initQuery : function(){
37881         this.doQuery(this.getRawValue());
37882     },
37883
37884     // private
37885     doForce : function(){
37886         if(this.el.dom.value.length > 0){
37887             this.el.dom.value =
37888                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37889             this.applyEmptyText();
37890         }
37891     },
37892
37893     /**
37894      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37895      * query allowing the query action to be canceled if needed.
37896      * @param {String} query The SQL query to execute
37897      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37898      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37899      * saved in the current store (defaults to false)
37900      */
37901     doQuery : function(q, forceAll){
37902         if(q === undefined || q === null){
37903             q = '';
37904         }
37905         var qe = {
37906             query: q,
37907             forceAll: forceAll,
37908             combo: this,
37909             cancel:false
37910         };
37911         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37912             return false;
37913         }
37914         q = qe.query;
37915         forceAll = qe.forceAll;
37916         if(forceAll === true || (q.length >= this.minChars)){
37917             if(this.lastQuery != q || this.alwaysQuery){
37918                 this.lastQuery = q;
37919                 if(this.mode == 'local'){
37920                     this.selectedIndex = -1;
37921                     if(forceAll){
37922                         this.store.clearFilter();
37923                     }else{
37924                         this.store.filter(this.displayField, q);
37925                     }
37926                     this.onLoad();
37927                 }else{
37928                     this.store.baseParams[this.queryParam] = q;
37929                     this.store.load({
37930                         params: this.getParams(q)
37931                     });
37932                     this.expand();
37933                 }
37934             }else{
37935                 this.selectedIndex = -1;
37936                 this.onLoad();   
37937             }
37938         }
37939     },
37940
37941     // private
37942     getParams : function(q){
37943         var p = {};
37944         //p[this.queryParam] = q;
37945         if(this.pageSize){
37946             p.start = 0;
37947             p.limit = this.pageSize;
37948         }
37949         return p;
37950     },
37951
37952     /**
37953      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
37954      */
37955     collapse : function(){
37956         if(!this.isExpanded()){
37957             return;
37958         }
37959         this.list.hide();
37960         Roo.get(document).un('mousedown', this.collapseIf, this);
37961         Roo.get(document).un('mousewheel', this.collapseIf, this);
37962         if (!this.editable) {
37963             Roo.get(document).un('keydown', this.listKeyPress, this);
37964         }
37965         this.fireEvent('collapse', this);
37966     },
37967
37968     // private
37969     collapseIf : function(e){
37970         if(!e.within(this.wrap) && !e.within(this.list)){
37971             this.collapse();
37972         }
37973     },
37974
37975     /**
37976      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
37977      */
37978     expand : function(){
37979         if(this.isExpanded() || !this.hasFocus){
37980             return;
37981         }
37982         this.list.alignTo(this.el, this.listAlign);
37983         this.list.show();
37984         Roo.get(document).on('mousedown', this.collapseIf, this);
37985         Roo.get(document).on('mousewheel', this.collapseIf, this);
37986         if (!this.editable) {
37987             Roo.get(document).on('keydown', this.listKeyPress, this);
37988         }
37989         
37990         this.fireEvent('expand', this);
37991     },
37992
37993     // private
37994     // Implements the default empty TriggerField.onTriggerClick function
37995     onTriggerClick : function(){
37996         if(this.disabled){
37997             return;
37998         }
37999         if(this.isExpanded()){
38000             this.collapse();
38001             if (!this.blockFocus) {
38002                 this.el.focus();
38003             }
38004             
38005         }else {
38006             this.hasFocus = true;
38007             if(this.triggerAction == 'all') {
38008                 this.doQuery(this.allQuery, true);
38009             } else {
38010                 this.doQuery(this.getRawValue());
38011             }
38012             if (!this.blockFocus) {
38013                 this.el.focus();
38014             }
38015         }
38016     },
38017     listKeyPress : function(e)
38018     {
38019         //Roo.log('listkeypress');
38020         // scroll to first matching element based on key pres..
38021         if (e.isSpecialKey()) {
38022             return false;
38023         }
38024         var k = String.fromCharCode(e.getKey()).toUpperCase();
38025         //Roo.log(k);
38026         var match  = false;
38027         var csel = this.view.getSelectedNodes();
38028         var cselitem = false;
38029         if (csel.length) {
38030             var ix = this.view.indexOf(csel[0]);
38031             cselitem  = this.store.getAt(ix);
38032             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38033                 cselitem = false;
38034             }
38035             
38036         }
38037         
38038         this.store.each(function(v) { 
38039             if (cselitem) {
38040                 // start at existing selection.
38041                 if (cselitem.id == v.id) {
38042                     cselitem = false;
38043                 }
38044                 return;
38045             }
38046                 
38047             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38048                 match = this.store.indexOf(v);
38049                 return false;
38050             }
38051         }, this);
38052         
38053         if (match === false) {
38054             return true; // no more action?
38055         }
38056         // scroll to?
38057         this.view.select(match);
38058         var sn = Roo.get(this.view.getSelectedNodes()[0])
38059         sn.scrollIntoView(sn.dom.parentNode, false);
38060     }
38061
38062     /** 
38063     * @cfg {Boolean} grow 
38064     * @hide 
38065     */
38066     /** 
38067     * @cfg {Number} growMin 
38068     * @hide 
38069     */
38070     /** 
38071     * @cfg {Number} growMax 
38072     * @hide 
38073     */
38074     /**
38075      * @hide
38076      * @method autoSize
38077      */
38078 });/*
38079  * Based on:
38080  * Ext JS Library 1.1.1
38081  * Copyright(c) 2006-2007, Ext JS, LLC.
38082  *
38083  * Originally Released Under LGPL - original licence link has changed is not relivant.
38084  *
38085  * Fork - LGPL
38086  * <script type="text/javascript">
38087  */
38088 /**
38089  * @class Roo.form.Checkbox
38090  * @extends Roo.form.Field
38091  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38092  * @constructor
38093  * Creates a new Checkbox
38094  * @param {Object} config Configuration options
38095  */
38096 Roo.form.Checkbox = function(config){
38097     Roo.form.Checkbox.superclass.constructor.call(this, config);
38098     this.addEvents({
38099         /**
38100          * @event check
38101          * Fires when the checkbox is checked or unchecked.
38102              * @param {Roo.form.Checkbox} this This checkbox
38103              * @param {Boolean} checked The new checked value
38104              */
38105         check : true
38106     });
38107 };
38108
38109 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38110     /**
38111      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38112      */
38113     focusClass : undefined,
38114     /**
38115      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38116      */
38117     fieldClass: "x-form-field",
38118     /**
38119      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38120      */
38121     checked: false,
38122     /**
38123      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38124      * {tag: "input", type: "checkbox", autocomplete: "off"})
38125      */
38126     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38127     /**
38128      * @cfg {String} boxLabel The text that appears beside the checkbox
38129      */
38130     boxLabel : "",
38131     /**
38132      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38133      */  
38134     inputValue : '1',
38135     /**
38136      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38137      */
38138      valueOff: '0', // value when not checked..
38139
38140     actionMode : 'viewEl', 
38141     //
38142     // private
38143     itemCls : 'x-menu-check-item x-form-item',
38144     groupClass : 'x-menu-group-item',
38145     inputType : 'hidden',
38146     
38147     
38148     inSetChecked: false, // check that we are not calling self...
38149     
38150     inputElement: false, // real input element?
38151     basedOn: false, // ????
38152     
38153     isFormField: true, // not sure where this is needed!!!!
38154
38155     onResize : function(){
38156         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38157         if(!this.boxLabel){
38158             this.el.alignTo(this.wrap, 'c-c');
38159         }
38160     },
38161
38162     initEvents : function(){
38163         Roo.form.Checkbox.superclass.initEvents.call(this);
38164         this.el.on("click", this.onClick,  this);
38165         this.el.on("change", this.onClick,  this);
38166     },
38167
38168
38169     getResizeEl : function(){
38170         return this.wrap;
38171     },
38172
38173     getPositionEl : function(){
38174         return this.wrap;
38175     },
38176
38177     // private
38178     onRender : function(ct, position){
38179         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38180         /*
38181         if(this.inputValue !== undefined){
38182             this.el.dom.value = this.inputValue;
38183         }
38184         */
38185         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38186         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38187         var viewEl = this.wrap.createChild({ 
38188             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38189         this.viewEl = viewEl;   
38190         this.wrap.on('click', this.onClick,  this); 
38191         
38192         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38193         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38194         
38195         
38196         
38197         if(this.boxLabel){
38198             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38199         //    viewEl.on('click', this.onClick,  this); 
38200         }
38201         //if(this.checked){
38202             this.setChecked(this.checked);
38203         //}else{
38204             //this.checked = this.el.dom;
38205         //}
38206
38207     },
38208
38209     // private
38210     initValue : Roo.emptyFn,
38211
38212     /**
38213      * Returns the checked state of the checkbox.
38214      * @return {Boolean} True if checked, else false
38215      */
38216     getValue : function(){
38217         if(this.el){
38218             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38219         }
38220         return this.valueOff;
38221         
38222     },
38223
38224         // private
38225     onClick : function(){ 
38226         this.setChecked(!this.checked);
38227
38228         //if(this.el.dom.checked != this.checked){
38229         //    this.setValue(this.el.dom.checked);
38230        // }
38231     },
38232
38233     /**
38234      * Sets the checked state of the checkbox.
38235      * On is always based on a string comparison between inputValue and the param.
38236      * @param {Boolean/String} value - the value to set 
38237      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38238      */
38239     setValue : function(v,suppressEvent){
38240         
38241         
38242         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38243         //if(this.el && this.el.dom){
38244         //    this.el.dom.checked = this.checked;
38245         //    this.el.dom.defaultChecked = this.checked;
38246         //}
38247         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38248         //this.fireEvent("check", this, this.checked);
38249     },
38250     // private..
38251     setChecked : function(state,suppressEvent)
38252     {
38253         if (this.inSetChecked) {
38254             this.checked = state;
38255             return;
38256         }
38257         
38258     
38259         if(this.wrap){
38260             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38261         }
38262         this.checked = state;
38263         if(suppressEvent !== true){
38264             this.fireEvent('check', this, state);
38265         }
38266         this.inSetChecked = true;
38267         this.el.dom.value = state ? this.inputValue : this.valueOff;
38268         this.inSetChecked = false;
38269         
38270     },
38271     // handle setting of hidden value by some other method!!?!?
38272     setFromHidden: function()
38273     {
38274         if(!this.el){
38275             return;
38276         }
38277         //console.log("SET FROM HIDDEN");
38278         //alert('setFrom hidden');
38279         this.setValue(this.el.dom.value);
38280     },
38281     
38282     onDestroy : function()
38283     {
38284         if(this.viewEl){
38285             Roo.get(this.viewEl).remove();
38286         }
38287          
38288         Roo.form.Checkbox.superclass.onDestroy.call(this);
38289     }
38290
38291 });/*
38292  * Based on:
38293  * Ext JS Library 1.1.1
38294  * Copyright(c) 2006-2007, Ext JS, LLC.
38295  *
38296  * Originally Released Under LGPL - original licence link has changed is not relivant.
38297  *
38298  * Fork - LGPL
38299  * <script type="text/javascript">
38300  */
38301  
38302 /**
38303  * @class Roo.form.Radio
38304  * @extends Roo.form.Checkbox
38305  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38306  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38307  * @constructor
38308  * Creates a new Radio
38309  * @param {Object} config Configuration options
38310  */
38311 Roo.form.Radio = function(){
38312     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38313 };
38314 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38315     inputType: 'radio',
38316
38317     /**
38318      * If this radio is part of a group, it will return the selected value
38319      * @return {String}
38320      */
38321     getGroupValue : function(){
38322         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38323     }
38324 });//<script type="text/javascript">
38325
38326 /*
38327  * Ext JS Library 1.1.1
38328  * Copyright(c) 2006-2007, Ext JS, LLC.
38329  * licensing@extjs.com
38330  * 
38331  * http://www.extjs.com/license
38332  */
38333  
38334  /*
38335   * 
38336   * Known bugs:
38337   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38338   * - IE ? - no idea how much works there.
38339   * 
38340   * 
38341   * 
38342   */
38343  
38344
38345 /**
38346  * @class Ext.form.HtmlEditor
38347  * @extends Ext.form.Field
38348  * Provides a lightweight HTML Editor component.
38349  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38350  * 
38351  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38352  * supported by this editor.</b><br/><br/>
38353  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38354  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38355  */
38356 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38357       /**
38358      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38359      */
38360     toolbars : false,
38361     /**
38362      * @cfg {String} createLinkText The default text for the create link prompt
38363      */
38364     createLinkText : 'Please enter the URL for the link:',
38365     /**
38366      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38367      */
38368     defaultLinkValue : 'http:/'+'/',
38369    
38370     
38371     // id of frame..
38372     frameId: false,
38373     
38374     // private properties
38375     validationEvent : false,
38376     deferHeight: true,
38377     initialized : false,
38378     activated : false,
38379     sourceEditMode : false,
38380     onFocus : Roo.emptyFn,
38381     iframePad:3,
38382     hideMode:'offsets',
38383     defaultAutoCreate : {
38384         tag: "textarea",
38385         style:"width:500px;height:300px;",
38386         autocomplete: "off"
38387     },
38388
38389     // private
38390     initComponent : function(){
38391         this.addEvents({
38392             /**
38393              * @event initialize
38394              * Fires when the editor is fully initialized (including the iframe)
38395              * @param {HtmlEditor} this
38396              */
38397             initialize: true,
38398             /**
38399              * @event activate
38400              * Fires when the editor is first receives the focus. Any insertion must wait
38401              * until after this event.
38402              * @param {HtmlEditor} this
38403              */
38404             activate: true,
38405              /**
38406              * @event beforesync
38407              * Fires before the textarea is updated with content from the editor iframe. Return false
38408              * to cancel the sync.
38409              * @param {HtmlEditor} this
38410              * @param {String} html
38411              */
38412             beforesync: true,
38413              /**
38414              * @event beforepush
38415              * Fires before the iframe editor is updated with content from the textarea. Return false
38416              * to cancel the push.
38417              * @param {HtmlEditor} this
38418              * @param {String} html
38419              */
38420             beforepush: true,
38421              /**
38422              * @event sync
38423              * Fires when the textarea is updated with content from the editor iframe.
38424              * @param {HtmlEditor} this
38425              * @param {String} html
38426              */
38427             sync: true,
38428              /**
38429              * @event push
38430              * Fires when the iframe editor is updated with content from the textarea.
38431              * @param {HtmlEditor} this
38432              * @param {String} html
38433              */
38434             push: true,
38435              /**
38436              * @event editmodechange
38437              * Fires when the editor switches edit modes
38438              * @param {HtmlEditor} this
38439              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38440              */
38441             editmodechange: true,
38442             /**
38443              * @event editorevent
38444              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38445              * @param {HtmlEditor} this
38446              */
38447             editorevent: true
38448         })
38449     },
38450
38451     /**
38452      * Protected method that will not generally be called directly. It
38453      * is called when the editor creates its toolbar. Override this method if you need to
38454      * add custom toolbar buttons.
38455      * @param {HtmlEditor} editor
38456      */
38457     createToolbar : function(editor){
38458         if (!editor.toolbars || !editor.toolbars.length) {
38459             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38460         }
38461         
38462         for (var i =0 ; i < editor.toolbars.length;i++) {
38463             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38464             editor.toolbars[i].init(editor);
38465         }
38466          
38467         
38468     },
38469
38470     /**
38471      * Protected method that will not generally be called directly. It
38472      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38473      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38474      */
38475     getDocMarkup : function(){
38476         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
38477     },
38478
38479     // private
38480     onRender : function(ct, position){
38481         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38482         this.el.dom.style.border = '0 none';
38483         this.el.dom.setAttribute('tabIndex', -1);
38484         this.el.addClass('x-hidden');
38485         if(Roo.isIE){ // fix IE 1px bogus margin
38486             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38487         }
38488         this.wrap = this.el.wrap({
38489             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38490         });
38491
38492         this.frameId = Roo.id();
38493         this.createToolbar(this);
38494         
38495         
38496         
38497         
38498       
38499         
38500         var iframe = this.wrap.createChild({
38501             tag: 'iframe',
38502             id: this.frameId,
38503             name: this.frameId,
38504             frameBorder : 'no',
38505             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38506         });
38507         
38508        // console.log(iframe);
38509         //this.wrap.dom.appendChild(iframe);
38510
38511         this.iframe = iframe.dom;
38512
38513          this.assignDocWin();
38514         
38515         this.doc.designMode = 'on';
38516        
38517         this.doc.open();
38518         this.doc.write(this.getDocMarkup());
38519         this.doc.close();
38520
38521         
38522         var task = { // must defer to wait for browser to be ready
38523             run : function(){
38524                 //console.log("run task?" + this.doc.readyState);
38525                 this.assignDocWin();
38526                 if(this.doc.body || this.doc.readyState == 'complete'){
38527                     try {
38528                         this.doc.designMode="on";
38529                     } catch (e) {
38530                         return;
38531                     }
38532                     Roo.TaskMgr.stop(task);
38533                     this.initEditor.defer(10, this);
38534                 }
38535             },
38536             interval : 10,
38537             duration:10000,
38538             scope: this
38539         };
38540         Roo.TaskMgr.start(task);
38541
38542         if(!this.width){
38543             this.setSize(this.el.getSize());
38544         }
38545     },
38546
38547     // private
38548     onResize : function(w, h){
38549         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38550         if(this.el && this.iframe){
38551             if(typeof w == 'number'){
38552                 var aw = w - this.wrap.getFrameWidth('lr');
38553                 this.el.setWidth(this.adjustWidth('textarea', aw));
38554                 this.iframe.style.width = aw + 'px';
38555             }
38556             if(typeof h == 'number'){
38557                 var tbh = 0;
38558                 for (var i =0; i < this.toolbars.length;i++) {
38559                     // fixme - ask toolbars for heights?
38560                     tbh += this.toolbars[i].tb.el.getHeight();
38561                 }
38562                 
38563                 
38564                 
38565                 
38566                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38567                 this.el.setHeight(this.adjustWidth('textarea', ah));
38568                 this.iframe.style.height = ah + 'px';
38569                 if(this.doc){
38570                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38571                 }
38572             }
38573         }
38574     },
38575
38576     /**
38577      * Toggles the editor between standard and source edit mode.
38578      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38579      */
38580     toggleSourceEdit : function(sourceEditMode){
38581         
38582         this.sourceEditMode = sourceEditMode === true;
38583         
38584         if(this.sourceEditMode){
38585           
38586             this.syncValue();
38587             this.iframe.className = 'x-hidden';
38588             this.el.removeClass('x-hidden');
38589             this.el.dom.removeAttribute('tabIndex');
38590             this.el.focus();
38591         }else{
38592              
38593             this.pushValue();
38594             this.iframe.className = '';
38595             this.el.addClass('x-hidden');
38596             this.el.dom.setAttribute('tabIndex', -1);
38597             this.deferFocus();
38598         }
38599         this.setSize(this.wrap.getSize());
38600         this.fireEvent('editmodechange', this, this.sourceEditMode);
38601     },
38602
38603     // private used internally
38604     createLink : function(){
38605         var url = prompt(this.createLinkText, this.defaultLinkValue);
38606         if(url && url != 'http:/'+'/'){
38607             this.relayCmd('createlink', url);
38608         }
38609     },
38610
38611     // private (for BoxComponent)
38612     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38613
38614     // private (for BoxComponent)
38615     getResizeEl : function(){
38616         return this.wrap;
38617     },
38618
38619     // private (for BoxComponent)
38620     getPositionEl : function(){
38621         return this.wrap;
38622     },
38623
38624     // private
38625     initEvents : function(){
38626         this.originalValue = this.getValue();
38627     },
38628
38629     /**
38630      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38631      * @method
38632      */
38633     markInvalid : Roo.emptyFn,
38634     /**
38635      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38636      * @method
38637      */
38638     clearInvalid : Roo.emptyFn,
38639
38640     setValue : function(v){
38641         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38642         this.pushValue();
38643     },
38644
38645     /**
38646      * Protected method that will not generally be called directly. If you need/want
38647      * custom HTML cleanup, this is the method you should override.
38648      * @param {String} html The HTML to be cleaned
38649      * return {String} The cleaned HTML
38650      */
38651     cleanHtml : function(html){
38652         html = String(html);
38653         if(html.length > 5){
38654             if(Roo.isSafari){ // strip safari nonsense
38655                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38656             }
38657         }
38658         if(html == '&nbsp;'){
38659             html = '';
38660         }
38661         return html;
38662     },
38663
38664     /**
38665      * Protected method that will not generally be called directly. Syncs the contents
38666      * of the editor iframe with the textarea.
38667      */
38668     syncValue : function(){
38669         if(this.initialized){
38670             var bd = (this.doc.body || this.doc.documentElement);
38671             this.cleanUpPaste();
38672             var html = bd.innerHTML;
38673             if(Roo.isSafari){
38674                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38675                 var m = bs.match(/text-align:(.*?);/i);
38676                 if(m && m[1]){
38677                     html = '<div style="'+m[0]+'">' + html + '</div>';
38678                 }
38679             }
38680             html = this.cleanHtml(html);
38681             if(this.fireEvent('beforesync', this, html) !== false){
38682                 this.el.dom.value = html;
38683                 this.fireEvent('sync', this, html);
38684             }
38685         }
38686     },
38687
38688     /**
38689      * Protected method that will not generally be called directly. Pushes the value of the textarea
38690      * into the iframe editor.
38691      */
38692     pushValue : function(){
38693         if(this.initialized){
38694             var v = this.el.dom.value;
38695             if(v.length < 1){
38696                 v = '&#160;';
38697             }
38698             
38699             if(this.fireEvent('beforepush', this, v) !== false){
38700                 var d = (this.doc.body || this.doc.documentElement);
38701                 d.innerHTML = v;
38702                 this.cleanUpPaste();
38703                 this.el.dom.value = d.innerHTML;
38704                 this.fireEvent('push', this, v);
38705             }
38706         }
38707     },
38708
38709     // private
38710     deferFocus : function(){
38711         this.focus.defer(10, this);
38712     },
38713
38714     // doc'ed in Field
38715     focus : function(){
38716         if(this.win && !this.sourceEditMode){
38717             this.win.focus();
38718         }else{
38719             this.el.focus();
38720         }
38721     },
38722     
38723     assignDocWin: function()
38724     {
38725         var iframe = this.iframe;
38726         
38727          if(Roo.isIE){
38728             this.doc = iframe.contentWindow.document;
38729             this.win = iframe.contentWindow;
38730         } else {
38731             if (!Roo.get(this.frameId)) {
38732                 return;
38733             }
38734             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38735             this.win = Roo.get(this.frameId).dom.contentWindow;
38736         }
38737     },
38738     
38739     // private
38740     initEditor : function(){
38741         //console.log("INIT EDITOR");
38742         this.assignDocWin();
38743         
38744         
38745         
38746         this.doc.designMode="on";
38747         this.doc.open();
38748         this.doc.write(this.getDocMarkup());
38749         this.doc.close();
38750         
38751         var dbody = (this.doc.body || this.doc.documentElement);
38752         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38753         // this copies styles from the containing element into thsi one..
38754         // not sure why we need all of this..
38755         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38756         ss['background-attachment'] = 'fixed'; // w3c
38757         dbody.bgProperties = 'fixed'; // ie
38758         Roo.DomHelper.applyStyles(dbody, ss);
38759         Roo.EventManager.on(this.doc, {
38760             'mousedown': this.onEditorEvent,
38761             'dblclick': this.onEditorEvent,
38762             'click': this.onEditorEvent,
38763             'keyup': this.onEditorEvent,
38764             buffer:100,
38765             scope: this
38766         });
38767         if(Roo.isGecko){
38768             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
38769         }
38770         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38771             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38772         }
38773         this.initialized = true;
38774
38775         this.fireEvent('initialize', this);
38776         this.pushValue();
38777     },
38778
38779     // private
38780     onDestroy : function(){
38781         
38782         
38783         
38784         if(this.rendered){
38785             
38786             for (var i =0; i < this.toolbars.length;i++) {
38787                 // fixme - ask toolbars for heights?
38788                 this.toolbars[i].onDestroy();
38789             }
38790             
38791             this.wrap.dom.innerHTML = '';
38792             this.wrap.remove();
38793         }
38794     },
38795
38796     // private
38797     onFirstFocus : function(){
38798         
38799         this.assignDocWin();
38800         
38801         
38802         this.activated = true;
38803         for (var i =0; i < this.toolbars.length;i++) {
38804             this.toolbars[i].onFirstFocus();
38805         }
38806        
38807         if(Roo.isGecko){ // prevent silly gecko errors
38808             this.win.focus();
38809             var s = this.win.getSelection();
38810             if(!s.focusNode || s.focusNode.nodeType != 3){
38811                 var r = s.getRangeAt(0);
38812                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38813                 r.collapse(true);
38814                 this.deferFocus();
38815             }
38816             try{
38817                 this.execCmd('useCSS', true);
38818                 this.execCmd('styleWithCSS', false);
38819             }catch(e){}
38820         }
38821         this.fireEvent('activate', this);
38822     },
38823
38824     // private
38825     adjustFont: function(btn){
38826         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38827         //if(Roo.isSafari){ // safari
38828         //    adjust *= 2;
38829        // }
38830         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38831         if(Roo.isSafari){ // safari
38832             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38833             v =  (v < 10) ? 10 : v;
38834             v =  (v > 48) ? 48 : v;
38835             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38836             
38837         }
38838         
38839         
38840         v = Math.max(1, v+adjust);
38841         
38842         this.execCmd('FontSize', v  );
38843     },
38844
38845     onEditorEvent : function(e){
38846         this.fireEvent('editorevent', this, e);
38847       //  this.updateToolbar();
38848         this.syncValue();
38849     },
38850
38851     insertTag : function(tg)
38852     {
38853         // could be a bit smarter... -> wrap the current selected tRoo..
38854         
38855         this.execCmd("formatblock",   tg);
38856         
38857     },
38858     
38859     insertText : function(txt)
38860     {
38861         
38862         
38863         range = this.createRange();
38864         range.deleteContents();
38865                //alert(Sender.getAttribute('label'));
38866                
38867         range.insertNode(this.doc.createTextNode(txt));
38868     } ,
38869     
38870     // private
38871     relayBtnCmd : function(btn){
38872         this.relayCmd(btn.cmd);
38873     },
38874
38875     /**
38876      * Executes a Midas editor command on the editor document and performs necessary focus and
38877      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
38878      * @param {String} cmd The Midas command
38879      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38880      */
38881     relayCmd : function(cmd, value){
38882         this.win.focus();
38883         this.execCmd(cmd, value);
38884         this.fireEvent('editorevent', this);
38885         //this.updateToolbar();
38886         this.deferFocus();
38887     },
38888
38889     /**
38890      * Executes a Midas editor command directly on the editor document.
38891      * For visual commands, you should use {@link #relayCmd} instead.
38892      * <b>This should only be called after the editor is initialized.</b>
38893      * @param {String} cmd The Midas command
38894      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38895      */
38896     execCmd : function(cmd, value){
38897         this.doc.execCommand(cmd, false, value === undefined ? null : value);
38898         this.syncValue();
38899     },
38900
38901    
38902     /**
38903      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
38904      * to insert tRoo.
38905      * @param {String} text
38906      */
38907     insertAtCursor : function(text){
38908         if(!this.activated){
38909             return;
38910         }
38911         if(Roo.isIE){
38912             this.win.focus();
38913             var r = this.doc.selection.createRange();
38914             if(r){
38915                 r.collapse(true);
38916                 r.pasteHTML(text);
38917                 this.syncValue();
38918                 this.deferFocus();
38919             }
38920         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
38921             this.win.focus();
38922             this.execCmd('InsertHTML', text);
38923             this.deferFocus();
38924         }
38925     },
38926  // private
38927     mozKeyPress : function(e){
38928         if(e.ctrlKey){
38929             var c = e.getCharCode(), cmd;
38930           
38931             if(c > 0){
38932                 c = String.fromCharCode(c).toLowerCase();
38933                 switch(c){
38934                     case 'b':
38935                         cmd = 'bold';
38936                     break;
38937                     case 'i':
38938                         cmd = 'italic';
38939                     break;
38940                     case 'u':
38941                         cmd = 'underline';
38942                     case 'v':
38943                         this.cleanUpPaste.defer(100, this);
38944                         return;
38945                     break;
38946                 }
38947                 if(cmd){
38948                     this.win.focus();
38949                     this.execCmd(cmd);
38950                     this.deferFocus();
38951                     e.preventDefault();
38952                 }
38953                 
38954             }
38955         }
38956     },
38957
38958     // private
38959     fixKeys : function(){ // load time branching for fastest keydown performance
38960         if(Roo.isIE){
38961             return function(e){
38962                 var k = e.getKey(), r;
38963                 if(k == e.TAB){
38964                     e.stopEvent();
38965                     r = this.doc.selection.createRange();
38966                     if(r){
38967                         r.collapse(true);
38968                         r.pasteHTML('&#160;&#160;&#160;&#160;');
38969                         this.deferFocus();
38970                     }
38971                     return;
38972                 }
38973                 
38974                 if(k == e.ENTER){
38975                     r = this.doc.selection.createRange();
38976                     if(r){
38977                         var target = r.parentElement();
38978                         if(!target || target.tagName.toLowerCase() != 'li'){
38979                             e.stopEvent();
38980                             r.pasteHTML('<br />');
38981                             r.collapse(false);
38982                             r.select();
38983                         }
38984                     }
38985                 }
38986                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38987                     this.cleanUpPaste.defer(100, this);
38988                     return;
38989                 }
38990                 
38991                 
38992             };
38993         }else if(Roo.isOpera){
38994             return function(e){
38995                 var k = e.getKey();
38996                 if(k == e.TAB){
38997                     e.stopEvent();
38998                     this.win.focus();
38999                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39000                     this.deferFocus();
39001                 }
39002                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39003                     this.cleanUpPaste.defer(100, this);
39004                     return;
39005                 }
39006                 
39007             };
39008         }else if(Roo.isSafari){
39009             return function(e){
39010                 var k = e.getKey();
39011                 
39012                 if(k == e.TAB){
39013                     e.stopEvent();
39014                     this.execCmd('InsertText','\t');
39015                     this.deferFocus();
39016                     return;
39017                 }
39018                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39019                     this.cleanUpPaste.defer(100, this);
39020                     return;
39021                 }
39022                 
39023              };
39024         }
39025     }(),
39026     
39027     getAllAncestors: function()
39028     {
39029         var p = this.getSelectedNode();
39030         var a = [];
39031         if (!p) {
39032             a.push(p); // push blank onto stack..
39033             p = this.getParentElement();
39034         }
39035         
39036         
39037         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39038             a.push(p);
39039             p = p.parentNode;
39040         }
39041         a.push(this.doc.body);
39042         return a;
39043     },
39044     lastSel : false,
39045     lastSelNode : false,
39046     
39047     
39048     getSelection : function() 
39049     {
39050         this.assignDocWin();
39051         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39052     },
39053     
39054     getSelectedNode: function() 
39055     {
39056         // this may only work on Gecko!!!
39057         
39058         // should we cache this!!!!
39059         
39060         
39061         
39062          
39063         var range = this.createRange(this.getSelection());
39064         
39065         if (Roo.isIE) {
39066             var parent = range.parentElement();
39067             while (true) {
39068                 var testRange = range.duplicate();
39069                 testRange.moveToElementText(parent);
39070                 if (testRange.inRange(range)) {
39071                     break;
39072                 }
39073                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39074                     break;
39075                 }
39076                 parent = parent.parentElement;
39077             }
39078             return parent;
39079         }
39080         
39081         
39082         var ar = range.endContainer.childNodes;
39083         if (!ar.length) {
39084             ar = range.commonAncestorContainer.childNodes;
39085             //alert(ar.length);
39086         }
39087         var nodes = [];
39088         var other_nodes = [];
39089         var has_other_nodes = false;
39090         for (var i=0;i<ar.length;i++) {
39091             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39092                 continue;
39093             }
39094             // fullly contained node.
39095             
39096             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39097                 nodes.push(ar[i]);
39098                 continue;
39099             }
39100             
39101             // probably selected..
39102             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39103                 other_nodes.push(ar[i]);
39104                 continue;
39105             }
39106             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39107                 continue;
39108             }
39109             
39110             
39111             has_other_nodes = true;
39112         }
39113         if (!nodes.length && other_nodes.length) {
39114             nodes= other_nodes;
39115         }
39116         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39117             return false;
39118         }
39119         
39120         return nodes[0];
39121     },
39122     createRange: function(sel)
39123     {
39124         // this has strange effects when using with 
39125         // top toolbar - not sure if it's a great idea.
39126         //this.editor.contentWindow.focus();
39127         if (typeof sel != "undefined") {
39128             try {
39129                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39130             } catch(e) {
39131                 return this.doc.createRange();
39132             }
39133         } else {
39134             return this.doc.createRange();
39135         }
39136     },
39137     getParentElement: function()
39138     {
39139         
39140         this.assignDocWin();
39141         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39142         
39143         var range = this.createRange(sel);
39144          
39145         try {
39146             var p = range.commonAncestorContainer;
39147             while (p.nodeType == 3) { // text node
39148                 p = p.parentNode;
39149             }
39150             return p;
39151         } catch (e) {
39152             return null;
39153         }
39154     
39155     },
39156     
39157     
39158     
39159     // BC Hacks - cause I cant work out what i was trying to do..
39160     rangeIntersectsNode : function(range, node)
39161     {
39162         var nodeRange = node.ownerDocument.createRange();
39163         try {
39164             nodeRange.selectNode(node);
39165         }
39166         catch (e) {
39167             nodeRange.selectNodeContents(node);
39168         }
39169
39170         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
39171                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
39172     },
39173     rangeCompareNode : function(range, node) {
39174         var nodeRange = node.ownerDocument.createRange();
39175         try {
39176             nodeRange.selectNode(node);
39177         } catch (e) {
39178             nodeRange.selectNodeContents(node);
39179         }
39180         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
39181         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
39182
39183         if (nodeIsBefore && !nodeIsAfter)
39184             return 0;
39185         if (!nodeIsBefore && nodeIsAfter)
39186             return 1;
39187         if (nodeIsBefore && nodeIsAfter)
39188             return 2;
39189
39190         return 3;
39191     },
39192
39193     // private? - in a new class?
39194     cleanUpPaste :  function()
39195     {
39196         // cleans up the whole document..
39197       //  console.log('cleanuppaste');
39198         this.cleanUpChildren(this.doc.body);
39199         
39200         
39201     },
39202     cleanUpChildren : function (n)
39203     {
39204         if (!n.childNodes.length) {
39205             return;
39206         }
39207         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39208            this.cleanUpChild(n.childNodes[i]);
39209         }
39210     },
39211     
39212     
39213         
39214     
39215     cleanUpChild : function (node)
39216     {
39217         //console.log(node);
39218         if (node.nodeName == "#text") {
39219             // clean up silly Windows -- stuff?
39220             return; 
39221         }
39222         if (node.nodeName == "#comment") {
39223             node.parentNode.removeChild(node);
39224             // clean up silly Windows -- stuff?
39225             return; 
39226         }
39227         
39228         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39229             // remove node.
39230             node.parentNode.removeChild(node);
39231             return;
39232             
39233         }
39234         if (!node.attributes || !node.attributes.length) {
39235             this.cleanUpChildren(node);
39236             return;
39237         }
39238         
39239         function cleanAttr(n,v)
39240         {
39241             
39242             if (v.match(/^\./) || v.match(/^\//)) {
39243                 return;
39244             }
39245             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39246                 return;
39247             }
39248             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39249             node.removeAttribute(n);
39250             
39251         }
39252         
39253         function cleanStyle(n,v)
39254         {
39255             if (v.match(/expression/)) { //XSS?? should we even bother..
39256                 node.removeAttribute(n);
39257                 return;
39258             }
39259             
39260             
39261             var parts = v.split(/;/);
39262             Roo.each(parts, function(p) {
39263                 p = p.replace(/\s+/g,'');
39264                 if (!p.length) {
39265                     return;
39266                 }
39267                 var l = p.split(':').shift().replace(/\s+/g,'');
39268                 
39269                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39270                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39271                     node.removeAttribute(n);
39272                     return false;
39273                 }
39274             });
39275             
39276             
39277         }
39278         
39279         
39280         for (var i = node.attributes.length-1; i > -1 ; i--) {
39281             var a = node.attributes[i];
39282             //console.log(a);
39283             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39284                 node.removeAttribute(a.name);
39285                 return;
39286             }
39287             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39288                 cleanAttr(a.name,a.value); // fixme..
39289                 return;
39290             }
39291             if (a.name == 'style') {
39292                 cleanStyle(a.name,a.value);
39293             }
39294             /// clean up MS crap..
39295             if (a.name == 'class') {
39296                 if (a.value.match(/^Mso/)) {
39297                     node.className = '';
39298                 }
39299             }
39300             
39301             // style cleanup!?
39302             // class cleanup?
39303             
39304         }
39305         
39306         
39307         this.cleanUpChildren(node);
39308         
39309         
39310     }
39311     
39312     
39313     // hide stuff that is not compatible
39314     /**
39315      * @event blur
39316      * @hide
39317      */
39318     /**
39319      * @event change
39320      * @hide
39321      */
39322     /**
39323      * @event focus
39324      * @hide
39325      */
39326     /**
39327      * @event specialkey
39328      * @hide
39329      */
39330     /**
39331      * @cfg {String} fieldClass @hide
39332      */
39333     /**
39334      * @cfg {String} focusClass @hide
39335      */
39336     /**
39337      * @cfg {String} autoCreate @hide
39338      */
39339     /**
39340      * @cfg {String} inputType @hide
39341      */
39342     /**
39343      * @cfg {String} invalidClass @hide
39344      */
39345     /**
39346      * @cfg {String} invalidText @hide
39347      */
39348     /**
39349      * @cfg {String} msgFx @hide
39350      */
39351     /**
39352      * @cfg {String} validateOnBlur @hide
39353      */
39354 });
39355
39356 Roo.form.HtmlEditor.white = [
39357         'area', 'br', 'img', 'input', 'hr', 'wbr',
39358         
39359        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39360        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39361        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39362        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39363        'table',   'ul',         'xmp', 
39364        
39365        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39366       'thead',   'tr', 
39367      
39368       'dir', 'menu', 'ol', 'ul', 'dl',
39369        
39370       'embed',  'object'
39371 ];
39372
39373
39374 Roo.form.HtmlEditor.black = [
39375     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39376         'applet', // 
39377         'base',   'basefont', 'bgsound', 'blink',  'body', 
39378         'frame',  'frameset', 'head',    'html',   'ilayer', 
39379         'iframe', 'layer',  'link',     'meta',    'object',   
39380         'script', 'style' ,'title',  'xml' // clean later..
39381 ];
39382 Roo.form.HtmlEditor.clean = [
39383     'script', 'style', 'title', 'xml'
39384 ];
39385
39386 // attributes..
39387
39388 Roo.form.HtmlEditor.ablack = [
39389     'on'
39390 ];
39391     
39392 Roo.form.HtmlEditor.aclean = [ 
39393     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39394 ];
39395
39396 // protocols..
39397 Roo.form.HtmlEditor.pwhite= [
39398         'http',  'https',  'mailto'
39399 ];
39400
39401 Roo.form.HtmlEditor.cwhite= [
39402         'text-align',
39403         'font-size'
39404 ];
39405
39406 // <script type="text/javascript">
39407 /*
39408  * Based on
39409  * Ext JS Library 1.1.1
39410  * Copyright(c) 2006-2007, Ext JS, LLC.
39411  *  
39412  
39413  */
39414
39415 /**
39416  * @class Roo.form.HtmlEditorToolbar1
39417  * Basic Toolbar
39418  * 
39419  * Usage:
39420  *
39421  new Roo.form.HtmlEditor({
39422     ....
39423     toolbars : [
39424         new Roo.form.HtmlEditorToolbar1({
39425             disable : { fonts: 1 , format: 1, ..., ... , ...],
39426             btns : [ .... ]
39427         })
39428     }
39429      
39430  * 
39431  * @cfg {Object} disable List of elements to disable..
39432  * @cfg {Array} btns List of additional buttons.
39433  * 
39434  * 
39435  * NEEDS Extra CSS? 
39436  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39437  */
39438  
39439 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39440 {
39441     
39442     Roo.apply(this, config);
39443     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39444     // dont call parent... till later.
39445 }
39446
39447 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39448     
39449     tb: false,
39450     
39451     rendered: false,
39452     
39453     editor : false,
39454     /**
39455      * @cfg {Object} disable  List of toolbar elements to disable
39456          
39457      */
39458     disable : false,
39459       /**
39460      * @cfg {Array} fontFamilies An array of available font families
39461      */
39462     fontFamilies : [
39463         'Arial',
39464         'Courier New',
39465         'Tahoma',
39466         'Times New Roman',
39467         'Verdana'
39468     ],
39469     
39470     specialChars : [
39471            "&#169;",
39472           "&#174;",     
39473           "&#8482;",    
39474           "&#163;" ,    
39475          // "&#8212;",    
39476           "&#8230;",    
39477           "&#247;" ,    
39478         //  "&#225;" ,     ?? a acute?
39479            "&#8364;"    , //Euro
39480        //   "&#8220;"    ,
39481         //  "&#8221;"    ,
39482         //  "&#8226;"    ,
39483           "&#176;"  //   , // degrees
39484
39485          // "&#233;"     , // e ecute
39486          // "&#250;"     , // u ecute?
39487     ],
39488     inputElements : [ 
39489             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39490             "input:submit", "input:button", "select", "textarea", "label" ],
39491     formats : [
39492         ["p"] ,  
39493         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39494         ["pre"],[ "code"], 
39495         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39496     ],
39497      /**
39498      * @cfg {String} defaultFont default font to use.
39499      */
39500     defaultFont: 'tahoma',
39501    
39502     fontSelect : false,
39503     
39504     
39505     formatCombo : false,
39506     
39507     init : function(editor)
39508     {
39509         this.editor = editor;
39510         
39511         
39512         var fid = editor.frameId;
39513         var etb = this;
39514         function btn(id, toggle, handler){
39515             var xid = fid + '-'+ id ;
39516             return {
39517                 id : xid,
39518                 cmd : id,
39519                 cls : 'x-btn-icon x-edit-'+id,
39520                 enableToggle:toggle !== false,
39521                 scope: editor, // was editor...
39522                 handler:handler||editor.relayBtnCmd,
39523                 clickEvent:'mousedown',
39524                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39525                 tabIndex:-1
39526             };
39527         }
39528         
39529         
39530         
39531         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39532         this.tb = tb;
39533          // stop form submits
39534         tb.el.on('click', function(e){
39535             e.preventDefault(); // what does this do?
39536         });
39537
39538         if(!this.disable.font && !Roo.isSafari){
39539             /* why no safari for fonts
39540             editor.fontSelect = tb.el.createChild({
39541                 tag:'select',
39542                 tabIndex: -1,
39543                 cls:'x-font-select',
39544                 html: editor.createFontOptions()
39545             });
39546             editor.fontSelect.on('change', function(){
39547                 var font = editor.fontSelect.dom.value;
39548                 editor.relayCmd('fontname', font);
39549                 editor.deferFocus();
39550             }, editor);
39551             tb.add(
39552                 editor.fontSelect.dom,
39553                 '-'
39554             );
39555             */
39556         };
39557         if(!this.disable.formats){
39558             this.formatCombo = new Roo.form.ComboBox({
39559                 store: new Roo.data.SimpleStore({
39560                     id : 'tag',
39561                     fields: ['tag'],
39562                     data : this.formats // from states.js
39563                 }),
39564                 blockFocus : true,
39565                 //autoCreate : {tag: "div",  size: "20"},
39566                 displayField:'tag',
39567                 typeAhead: false,
39568                 mode: 'local',
39569                 editable : false,
39570                 triggerAction: 'all',
39571                 emptyText:'Add tag',
39572                 selectOnFocus:true,
39573                 width:135,
39574                 listeners : {
39575                     'select': function(c, r, i) {
39576                         editor.insertTag(r.get('tag'));
39577                         editor.focus();
39578                     }
39579                 }
39580
39581             });
39582             tb.addField(this.formatCombo);
39583             
39584         }
39585         
39586         if(!this.disable.format){
39587             tb.add(
39588                 btn('bold'),
39589                 btn('italic'),
39590                 btn('underline')
39591             );
39592         };
39593         if(!this.disable.fontSize){
39594             tb.add(
39595                 '-',
39596                 
39597                 
39598                 btn('increasefontsize', false, editor.adjustFont),
39599                 btn('decreasefontsize', false, editor.adjustFont)
39600             );
39601         };
39602         
39603         
39604         if(this.disable.colors){
39605             tb.add(
39606                 '-', {
39607                     id:editor.frameId +'-forecolor',
39608                     cls:'x-btn-icon x-edit-forecolor',
39609                     clickEvent:'mousedown',
39610                     tooltip: this.buttonTips['forecolor'] || undefined,
39611                     tabIndex:-1,
39612                     menu : new Roo.menu.ColorMenu({
39613                         allowReselect: true,
39614                         focus: Roo.emptyFn,
39615                         value:'000000',
39616                         plain:true,
39617                         selectHandler: function(cp, color){
39618                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
39619                             editor.deferFocus();
39620                         },
39621                         scope: editor,
39622                         clickEvent:'mousedown'
39623                     })
39624                 }, {
39625                     id:editor.frameId +'backcolor',
39626                     cls:'x-btn-icon x-edit-backcolor',
39627                     clickEvent:'mousedown',
39628                     tooltip: this.buttonTips['backcolor'] || undefined,
39629                     tabIndex:-1,
39630                     menu : new Roo.menu.ColorMenu({
39631                         focus: Roo.emptyFn,
39632                         value:'FFFFFF',
39633                         plain:true,
39634                         allowReselect: true,
39635                         selectHandler: function(cp, color){
39636                             if(Roo.isGecko){
39637                                 editor.execCmd('useCSS', false);
39638                                 editor.execCmd('hilitecolor', color);
39639                                 editor.execCmd('useCSS', true);
39640                                 editor.deferFocus();
39641                             }else{
39642                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
39643                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
39644                                 editor.deferFocus();
39645                             }
39646                         },
39647                         scope:editor,
39648                         clickEvent:'mousedown'
39649                     })
39650                 }
39651             );
39652         };
39653         // now add all the items...
39654         
39655
39656         if(!this.disable.alignments){
39657             tb.add(
39658                 '-',
39659                 btn('justifyleft'),
39660                 btn('justifycenter'),
39661                 btn('justifyright')
39662             );
39663         };
39664
39665         //if(!Roo.isSafari){
39666             if(!this.disable.links){
39667                 tb.add(
39668                     '-',
39669                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
39670                 );
39671             };
39672
39673             if(!this.disable.lists){
39674                 tb.add(
39675                     '-',
39676                     btn('insertorderedlist'),
39677                     btn('insertunorderedlist')
39678                 );
39679             }
39680             if(!this.disable.sourceEdit){
39681                 tb.add(
39682                     '-',
39683                     btn('sourceedit', true, function(btn){
39684                         this.toggleSourceEdit(btn.pressed);
39685                     })
39686                 );
39687             }
39688         //}
39689         
39690         var smenu = { };
39691         // special menu.. - needs to be tidied up..
39692         if (!this.disable.special) {
39693             smenu = {
39694                 text: "&#169;",
39695                 cls: 'x-edit-none',
39696                 menu : {
39697                     items : []
39698                    }
39699             };
39700             for (var i =0; i < this.specialChars.length; i++) {
39701                 smenu.menu.items.push({
39702                     
39703                     html: this.specialChars[i],
39704                     handler: function(a,b) {
39705                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
39706                         
39707                     },
39708                     tabIndex:-1
39709                 });
39710             }
39711             
39712             
39713             tb.add(smenu);
39714             
39715             
39716         }
39717         if (this.btns) {
39718             for(var i =0; i< this.btns.length;i++) {
39719                 var b = this.btns[i];
39720                 b.cls =  'x-edit-none';
39721                 b.scope = editor;
39722                 tb.add(b);
39723             }
39724         
39725         }
39726         
39727         
39728         
39729         // disable everything...
39730         
39731         this.tb.items.each(function(item){
39732            if(item.id != editor.frameId+ '-sourceedit'){
39733                 item.disable();
39734             }
39735         });
39736         this.rendered = true;
39737         
39738         // the all the btns;
39739         editor.on('editorevent', this.updateToolbar, this);
39740         // other toolbars need to implement this..
39741         //editor.on('editmodechange', this.updateToolbar, this);
39742     },
39743     
39744     
39745     
39746     /**
39747      * Protected method that will not generally be called directly. It triggers
39748      * a toolbar update by reading the markup state of the current selection in the editor.
39749      */
39750     updateToolbar: function(){
39751
39752         if(!this.editor.activated){
39753             this.editor.onFirstFocus();
39754             return;
39755         }
39756
39757         var btns = this.tb.items.map, 
39758             doc = this.editor.doc,
39759             frameId = this.editor.frameId;
39760
39761         if(!this.disable.font && !Roo.isSafari){
39762             /*
39763             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
39764             if(name != this.fontSelect.dom.value){
39765                 this.fontSelect.dom.value = name;
39766             }
39767             */
39768         }
39769         if(!this.disable.format){
39770             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
39771             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
39772             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
39773         }
39774         if(!this.disable.alignments){
39775             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
39776             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
39777             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
39778         }
39779         if(!Roo.isSafari && !this.disable.lists){
39780             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
39781             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
39782         }
39783         
39784         var ans = this.editor.getAllAncestors();
39785         if (this.formatCombo) {
39786             
39787             
39788             var store = this.formatCombo.store;
39789             this.formatCombo.setValue("");
39790             for (var i =0; i < ans.length;i++) {
39791                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
39792                     // select it..
39793                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
39794                     break;
39795                 }
39796             }
39797         }
39798         
39799         
39800         
39801         // hides menus... - so this cant be on a menu...
39802         Roo.menu.MenuMgr.hideAll();
39803
39804         //this.editorsyncValue();
39805     },
39806    
39807     
39808     createFontOptions : function(){
39809         var buf = [], fs = this.fontFamilies, ff, lc;
39810         for(var i = 0, len = fs.length; i< len; i++){
39811             ff = fs[i];
39812             lc = ff.toLowerCase();
39813             buf.push(
39814                 '<option value="',lc,'" style="font-family:',ff,';"',
39815                     (this.defaultFont == lc ? ' selected="true">' : '>'),
39816                     ff,
39817                 '</option>'
39818             );
39819         }
39820         return buf.join('');
39821     },
39822     
39823     toggleSourceEdit : function(sourceEditMode){
39824         if(sourceEditMode === undefined){
39825             sourceEditMode = !this.sourceEditMode;
39826         }
39827         this.sourceEditMode = sourceEditMode === true;
39828         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
39829         // just toggle the button?
39830         if(btn.pressed !== this.editor.sourceEditMode){
39831             btn.toggle(this.editor.sourceEditMode);
39832             return;
39833         }
39834         
39835         if(this.sourceEditMode){
39836             this.tb.items.each(function(item){
39837                 if(item.cmd != 'sourceedit'){
39838                     item.disable();
39839                 }
39840             });
39841           
39842         }else{
39843             if(this.initialized){
39844                 this.tb.items.each(function(item){
39845                     item.enable();
39846                 });
39847             }
39848             
39849         }
39850         // tell the editor that it's been pressed..
39851         this.editor.toggleSourceEdit(sourceEditMode);
39852        
39853     },
39854      /**
39855      * Object collection of toolbar tooltips for the buttons in the editor. The key
39856      * is the command id associated with that button and the value is a valid QuickTips object.
39857      * For example:
39858 <pre><code>
39859 {
39860     bold : {
39861         title: 'Bold (Ctrl+B)',
39862         text: 'Make the selected text bold.',
39863         cls: 'x-html-editor-tip'
39864     },
39865     italic : {
39866         title: 'Italic (Ctrl+I)',
39867         text: 'Make the selected text italic.',
39868         cls: 'x-html-editor-tip'
39869     },
39870     ...
39871 </code></pre>
39872     * @type Object
39873      */
39874     buttonTips : {
39875         bold : {
39876             title: 'Bold (Ctrl+B)',
39877             text: 'Make the selected text bold.',
39878             cls: 'x-html-editor-tip'
39879         },
39880         italic : {
39881             title: 'Italic (Ctrl+I)',
39882             text: 'Make the selected text italic.',
39883             cls: 'x-html-editor-tip'
39884         },
39885         underline : {
39886             title: 'Underline (Ctrl+U)',
39887             text: 'Underline the selected text.',
39888             cls: 'x-html-editor-tip'
39889         },
39890         increasefontsize : {
39891             title: 'Grow Text',
39892             text: 'Increase the font size.',
39893             cls: 'x-html-editor-tip'
39894         },
39895         decreasefontsize : {
39896             title: 'Shrink Text',
39897             text: 'Decrease the font size.',
39898             cls: 'x-html-editor-tip'
39899         },
39900         backcolor : {
39901             title: 'Text Highlight Color',
39902             text: 'Change the background color of the selected text.',
39903             cls: 'x-html-editor-tip'
39904         },
39905         forecolor : {
39906             title: 'Font Color',
39907             text: 'Change the color of the selected text.',
39908             cls: 'x-html-editor-tip'
39909         },
39910         justifyleft : {
39911             title: 'Align Text Left',
39912             text: 'Align text to the left.',
39913             cls: 'x-html-editor-tip'
39914         },
39915         justifycenter : {
39916             title: 'Center Text',
39917             text: 'Center text in the editor.',
39918             cls: 'x-html-editor-tip'
39919         },
39920         justifyright : {
39921             title: 'Align Text Right',
39922             text: 'Align text to the right.',
39923             cls: 'x-html-editor-tip'
39924         },
39925         insertunorderedlist : {
39926             title: 'Bullet List',
39927             text: 'Start a bulleted list.',
39928             cls: 'x-html-editor-tip'
39929         },
39930         insertorderedlist : {
39931             title: 'Numbered List',
39932             text: 'Start a numbered list.',
39933             cls: 'x-html-editor-tip'
39934         },
39935         createlink : {
39936             title: 'Hyperlink',
39937             text: 'Make the selected text a hyperlink.',
39938             cls: 'x-html-editor-tip'
39939         },
39940         sourceedit : {
39941             title: 'Source Edit',
39942             text: 'Switch to source editing mode.',
39943             cls: 'x-html-editor-tip'
39944         }
39945     },
39946     // private
39947     onDestroy : function(){
39948         if(this.rendered){
39949             
39950             this.tb.items.each(function(item){
39951                 if(item.menu){
39952                     item.menu.removeAll();
39953                     if(item.menu.el){
39954                         item.menu.el.destroy();
39955                     }
39956                 }
39957                 item.destroy();
39958             });
39959              
39960         }
39961     },
39962     onFirstFocus: function() {
39963         this.tb.items.each(function(item){
39964            item.enable();
39965         });
39966     }
39967 });
39968
39969
39970
39971
39972 // <script type="text/javascript">
39973 /*
39974  * Based on
39975  * Ext JS Library 1.1.1
39976  * Copyright(c) 2006-2007, Ext JS, LLC.
39977  *  
39978  
39979  */
39980
39981  
39982 /**
39983  * @class Roo.form.HtmlEditor.ToolbarContext
39984  * Context Toolbar
39985  * 
39986  * Usage:
39987  *
39988  new Roo.form.HtmlEditor({
39989     ....
39990     toolbars : [
39991         new Roo.form.HtmlEditor.ToolbarStandard(),
39992         new Roo.form.HtmlEditor.ToolbarContext()
39993         })
39994     }
39995      
39996  * 
39997  * @config : {Object} disable List of elements to disable.. (not done yet.)
39998  * 
39999  * 
40000  */
40001
40002 Roo.form.HtmlEditor.ToolbarContext = function(config)
40003 {
40004     
40005     Roo.apply(this, config);
40006     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40007     // dont call parent... till later.
40008 }
40009 Roo.form.HtmlEditor.ToolbarContext.types = {
40010     'IMG' : {
40011         width : {
40012             title: "Width",
40013             width: 40
40014         },
40015         height:  {
40016             title: "Height",
40017             width: 40
40018         },
40019         align: {
40020             title: "Align",
40021             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40022             width : 80
40023             
40024         },
40025         border: {
40026             title: "Border",
40027             width: 40
40028         },
40029         alt: {
40030             title: "Alt",
40031             width: 120
40032         },
40033         src : {
40034             title: "Src",
40035             width: 220
40036         }
40037         
40038     },
40039     'A' : {
40040         name : {
40041             title: "Name",
40042             width: 50
40043         },
40044         href:  {
40045             title: "Href",
40046             width: 220
40047         } // border?
40048         
40049     },
40050     'TABLE' : {
40051         rows : {
40052             title: "Rows",
40053             width: 20
40054         },
40055         cols : {
40056             title: "Cols",
40057             width: 20
40058         },
40059         width : {
40060             title: "Width",
40061             width: 40
40062         },
40063         height : {
40064             title: "Height",
40065             width: 40
40066         },
40067         border : {
40068             title: "Border",
40069             width: 20
40070         }
40071     },
40072     'TD' : {
40073         width : {
40074             title: "Width",
40075             width: 40
40076         },
40077         height : {
40078             title: "Height",
40079             width: 40
40080         },   
40081         align: {
40082             title: "Align",
40083             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40084             width: 40
40085         },
40086         valign: {
40087             title: "Valign",
40088             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40089             width: 40
40090         },
40091         colspan: {
40092             title: "Colspan",
40093             width: 20
40094             
40095         }
40096     },
40097     'INPUT' : {
40098         name : {
40099             title: "name",
40100             width: 120
40101         },
40102         value : {
40103             title: "Value",
40104             width: 120
40105         },
40106         width : {
40107             title: "Width",
40108             width: 40
40109         }
40110     },
40111     'LABEL' : {
40112         'for' : {
40113             title: "For",
40114             width: 120
40115         }
40116     },
40117     'TEXTAREA' : {
40118           name : {
40119             title: "name",
40120             width: 120
40121         },
40122         rows : {
40123             title: "Rows",
40124             width: 20
40125         },
40126         cols : {
40127             title: "Cols",
40128             width: 20
40129         }
40130     },
40131     'SELECT' : {
40132         name : {
40133             title: "name",
40134             width: 120
40135         },
40136         selectoptions : {
40137             title: "Options",
40138             width: 200
40139         }
40140     },
40141     'BODY' : {
40142         title : {
40143             title: "title",
40144             width: 120,
40145             disabled : true
40146         }
40147     }
40148 };
40149
40150
40151
40152 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40153     
40154     tb: false,
40155     
40156     rendered: false,
40157     
40158     editor : false,
40159     /**
40160      * @cfg {Object} disable  List of toolbar elements to disable
40161          
40162      */
40163     disable : false,
40164     
40165     
40166     
40167     toolbars : false,
40168     
40169     init : function(editor)
40170     {
40171         this.editor = editor;
40172         
40173         
40174         var fid = editor.frameId;
40175         var etb = this;
40176         function btn(id, toggle, handler){
40177             var xid = fid + '-'+ id ;
40178             return {
40179                 id : xid,
40180                 cmd : id,
40181                 cls : 'x-btn-icon x-edit-'+id,
40182                 enableToggle:toggle !== false,
40183                 scope: editor, // was editor...
40184                 handler:handler||editor.relayBtnCmd,
40185                 clickEvent:'mousedown',
40186                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40187                 tabIndex:-1
40188             };
40189         }
40190         // create a new element.
40191         var wdiv = editor.wrap.createChild({
40192                 tag: 'div'
40193             }, editor.wrap.dom.firstChild.nextSibling, true);
40194         
40195         // can we do this more than once??
40196         
40197          // stop form submits
40198       
40199  
40200         // disable everything...
40201         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40202         this.toolbars = {};
40203            
40204         for (var i in  ty) {
40205           
40206             this.toolbars[i] = this.buildToolbar(ty[i],i);
40207         }
40208         this.tb = this.toolbars.BODY;
40209         this.tb.el.show();
40210         
40211          
40212         this.rendered = true;
40213         
40214         // the all the btns;
40215         editor.on('editorevent', this.updateToolbar, this);
40216         // other toolbars need to implement this..
40217         //editor.on('editmodechange', this.updateToolbar, this);
40218     },
40219     
40220     
40221     
40222     /**
40223      * Protected method that will not generally be called directly. It triggers
40224      * a toolbar update by reading the markup state of the current selection in the editor.
40225      */
40226     updateToolbar: function(){
40227
40228         if(!this.editor.activated){
40229             this.editor.onFirstFocus();
40230             return;
40231         }
40232
40233         
40234         var ans = this.editor.getAllAncestors();
40235         
40236         // pick
40237         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40238         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40239         sel = sel ? sel : this.editor.doc.body;
40240         sel = sel.tagName.length ? sel : this.editor.doc.body;
40241         var tn = sel.tagName.toUpperCase();
40242         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40243         tn = sel.tagName.toUpperCase();
40244         if (this.tb.name  == tn) {
40245             return; // no change
40246         }
40247         this.tb.el.hide();
40248         ///console.log("show: " + tn);
40249         this.tb =  this.toolbars[tn];
40250         this.tb.el.show();
40251         this.tb.fields.each(function(e) {
40252             e.setValue(sel.getAttribute(e.name));
40253         });
40254         this.tb.selectedNode = sel;
40255         
40256         
40257         Roo.menu.MenuMgr.hideAll();
40258
40259         //this.editorsyncValue();
40260     },
40261    
40262        
40263     // private
40264     onDestroy : function(){
40265         if(this.rendered){
40266             
40267             this.tb.items.each(function(item){
40268                 if(item.menu){
40269                     item.menu.removeAll();
40270                     if(item.menu.el){
40271                         item.menu.el.destroy();
40272                     }
40273                 }
40274                 item.destroy();
40275             });
40276              
40277         }
40278     },
40279     onFirstFocus: function() {
40280         // need to do this for all the toolbars..
40281         this.tb.items.each(function(item){
40282            item.enable();
40283         });
40284     },
40285     buildToolbar: function(tlist, nm)
40286     {
40287         var editor = this.editor;
40288          // create a new element.
40289         var wdiv = editor.wrap.createChild({
40290                 tag: 'div'
40291             }, editor.wrap.dom.firstChild.nextSibling, true);
40292         
40293        
40294         var tb = new Roo.Toolbar(wdiv);
40295         tb.add(nm+ ":&nbsp;");
40296         for (var i in tlist) {
40297             var item = tlist[i];
40298             tb.add(item.title + ":&nbsp;");
40299             if (item.opts) {
40300                 // fixme
40301                 
40302               
40303                 tb.addField( new Roo.form.ComboBox({
40304                     store: new Roo.data.SimpleStore({
40305                         id : 'val',
40306                         fields: ['val'],
40307                         data : item.opts // from states.js
40308                     }),
40309                     name : i,
40310                     displayField:'val',
40311                     typeAhead: false,
40312                     mode: 'local',
40313                     editable : false,
40314                     triggerAction: 'all',
40315                     emptyText:'Select',
40316                     selectOnFocus:true,
40317                     width: item.width ? item.width  : 130,
40318                     listeners : {
40319                         'select': function(c, r, i) {
40320                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40321                         }
40322                     }
40323
40324                 }));
40325                 continue;
40326                     
40327                 
40328                 
40329                 
40330                 
40331                 tb.addField( new Roo.form.TextField({
40332                     name: i,
40333                     width: 100,
40334                     //allowBlank:false,
40335                     value: ''
40336                 }));
40337                 continue;
40338             }
40339             tb.addField( new Roo.form.TextField({
40340                 name: i,
40341                 width: item.width,
40342                 //allowBlank:true,
40343                 value: '',
40344                 listeners: {
40345                     'change' : function(f, nv, ov) {
40346                         tb.selectedNode.setAttribute(f.name, nv);
40347                     }
40348                 }
40349             }));
40350              
40351         }
40352         tb.el.on('click', function(e){
40353             e.preventDefault(); // what does this do?
40354         });
40355         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40356         tb.el.hide();
40357         tb.name = nm;
40358         // dont need to disable them... as they will get hidden
40359         return tb;
40360          
40361         
40362     }
40363     
40364     
40365     
40366     
40367 });
40368
40369
40370
40371
40372
40373 /*
40374  * Based on:
40375  * Ext JS Library 1.1.1
40376  * Copyright(c) 2006-2007, Ext JS, LLC.
40377  *
40378  * Originally Released Under LGPL - original licence link has changed is not relivant.
40379  *
40380  * Fork - LGPL
40381  * <script type="text/javascript">
40382  */
40383  
40384 /**
40385  * @class Roo.form.BasicForm
40386  * @extends Roo.util.Observable
40387  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
40388  * @constructor
40389  * @param {String/HTMLElement/Roo.Element} el The form element or its id
40390  * @param {Object} config Configuration options
40391  */
40392 Roo.form.BasicForm = function(el, config){
40393     this.allItems = [];
40394     this.childForms = [];
40395     Roo.apply(this, config);
40396     /*
40397      * The Roo.form.Field items in this form.
40398      * @type MixedCollection
40399      */
40400      
40401      
40402     this.items = new Roo.util.MixedCollection(false, function(o){
40403         return o.id || (o.id = Roo.id());
40404     });
40405     this.addEvents({
40406         /**
40407          * @event beforeaction
40408          * Fires before any action is performed. Return false to cancel the action.
40409          * @param {Form} this
40410          * @param {Action} action The action to be performed
40411          */
40412         beforeaction: true,
40413         /**
40414          * @event actionfailed
40415          * Fires when an action fails.
40416          * @param {Form} this
40417          * @param {Action} action The action that failed
40418          */
40419         actionfailed : true,
40420         /**
40421          * @event actioncomplete
40422          * Fires when an action is completed.
40423          * @param {Form} this
40424          * @param {Action} action The action that completed
40425          */
40426         actioncomplete : true
40427     });
40428     if(el){
40429         this.initEl(el);
40430     }
40431     Roo.form.BasicForm.superclass.constructor.call(this);
40432 };
40433
40434 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
40435     /**
40436      * @cfg {String} method
40437      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
40438      */
40439     /**
40440      * @cfg {DataReader} reader
40441      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
40442      * This is optional as there is built-in support for processing JSON.
40443      */
40444     /**
40445      * @cfg {DataReader} errorReader
40446      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
40447      * This is completely optional as there is built-in support for processing JSON.
40448      */
40449     /**
40450      * @cfg {String} url
40451      * The URL to use for form actions if one isn't supplied in the action options.
40452      */
40453     /**
40454      * @cfg {Boolean} fileUpload
40455      * Set to true if this form is a file upload.
40456      */
40457      
40458     /**
40459      * @cfg {Object} baseParams
40460      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
40461      */
40462      /**
40463      
40464     /**
40465      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
40466      */
40467     timeout: 30,
40468
40469     // private
40470     activeAction : null,
40471
40472     /**
40473      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
40474      * or setValues() data instead of when the form was first created.
40475      */
40476     trackResetOnLoad : false,
40477     
40478     
40479     /**
40480      * childForms - used for multi-tab forms
40481      * @type {Array}
40482      */
40483     childForms : false,
40484     
40485     /**
40486      * allItems - full list of fields.
40487      * @type {Array}
40488      */
40489     allItems : false,
40490     
40491     /**
40492      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
40493      * element by passing it or its id or mask the form itself by passing in true.
40494      * @type Mixed
40495      */
40496     waitMsgTarget : false,
40497
40498     // private
40499     initEl : function(el){
40500         this.el = Roo.get(el);
40501         this.id = this.el.id || Roo.id();
40502         this.el.on('submit', this.onSubmit, this);
40503         this.el.addClass('x-form');
40504     },
40505
40506     // private
40507     onSubmit : function(e){
40508         e.stopEvent();
40509     },
40510
40511     /**
40512      * Returns true if client-side validation on the form is successful.
40513      * @return Boolean
40514      */
40515     isValid : function(){
40516         var valid = true;
40517         this.items.each(function(f){
40518            if(!f.validate()){
40519                valid = false;
40520            }
40521         });
40522         return valid;
40523     },
40524
40525     /**
40526      * Returns true if any fields in this form have changed since their original load.
40527      * @return Boolean
40528      */
40529     isDirty : function(){
40530         var dirty = false;
40531         this.items.each(function(f){
40532            if(f.isDirty()){
40533                dirty = true;
40534                return false;
40535            }
40536         });
40537         return dirty;
40538     },
40539
40540     /**
40541      * Performs a predefined action (submit or load) or custom actions you define on this form.
40542      * @param {String} actionName The name of the action type
40543      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
40544      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
40545      * accept other config options):
40546      * <pre>
40547 Property          Type             Description
40548 ----------------  ---------------  ----------------------------------------------------------------------------------
40549 url               String           The url for the action (defaults to the form's url)
40550 method            String           The form method to use (defaults to the form's method, or POST if not defined)
40551 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
40552 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
40553                                    validate the form on the client (defaults to false)
40554      * </pre>
40555      * @return {BasicForm} this
40556      */
40557     doAction : function(action, options){
40558         if(typeof action == 'string'){
40559             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
40560         }
40561         if(this.fireEvent('beforeaction', this, action) !== false){
40562             this.beforeAction(action);
40563             action.run.defer(100, action);
40564         }
40565         return this;
40566     },
40567
40568     /**
40569      * Shortcut to do a submit action.
40570      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40571      * @return {BasicForm} this
40572      */
40573     submit : function(options){
40574         this.doAction('submit', options);
40575         return this;
40576     },
40577
40578     /**
40579      * Shortcut to do a load action.
40580      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40581      * @return {BasicForm} this
40582      */
40583     load : function(options){
40584         this.doAction('load', options);
40585         return this;
40586     },
40587
40588     /**
40589      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
40590      * @param {Record} record The record to edit
40591      * @return {BasicForm} this
40592      */
40593     updateRecord : function(record){
40594         record.beginEdit();
40595         var fs = record.fields;
40596         fs.each(function(f){
40597             var field = this.findField(f.name);
40598             if(field){
40599                 record.set(f.name, field.getValue());
40600             }
40601         }, this);
40602         record.endEdit();
40603         return this;
40604     },
40605
40606     /**
40607      * Loads an Roo.data.Record into this form.
40608      * @param {Record} record The record to load
40609      * @return {BasicForm} this
40610      */
40611     loadRecord : function(record){
40612         this.setValues(record.data);
40613         return this;
40614     },
40615
40616     // private
40617     beforeAction : function(action){
40618         var o = action.options;
40619         
40620        
40621         if(this.waitMsgTarget === true){
40622             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
40623         }else if(this.waitMsgTarget){
40624             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
40625             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
40626         }else {
40627             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
40628         }
40629          
40630     },
40631
40632     // private
40633     afterAction : function(action, success){
40634         this.activeAction = null;
40635         var o = action.options;
40636         
40637         if(this.waitMsgTarget === true){
40638             this.el.unmask();
40639         }else if(this.waitMsgTarget){
40640             this.waitMsgTarget.unmask();
40641         }else{
40642             Roo.MessageBox.updateProgress(1);
40643             Roo.MessageBox.hide();
40644         }
40645          
40646         if(success){
40647             if(o.reset){
40648                 this.reset();
40649             }
40650             Roo.callback(o.success, o.scope, [this, action]);
40651             this.fireEvent('actioncomplete', this, action);
40652             
40653         }else{
40654             Roo.callback(o.failure, o.scope, [this, action]);
40655             // show an error message if no failed handler is set..
40656             if (!this.hasListener('actionfailed')) {
40657                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
40658             }
40659             
40660             this.fireEvent('actionfailed', this, action);
40661         }
40662         
40663     },
40664
40665     /**
40666      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
40667      * @param {String} id The value to search for
40668      * @return Field
40669      */
40670     findField : function(id){
40671         var field = this.items.get(id);
40672         if(!field){
40673             this.items.each(function(f){
40674                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
40675                     field = f;
40676                     return false;
40677                 }
40678             });
40679         }
40680         return field || null;
40681     },
40682
40683     /**
40684      * Add a secondary form to this one, 
40685      * Used to provide tabbed forms. One form is primary, with hidden values 
40686      * which mirror the elements from the other forms.
40687      * 
40688      * @param {Roo.form.Form} form to add.
40689      * 
40690      */
40691     addForm : function(form)
40692     {
40693        
40694         if (this.childForms.indexOf(form) > -1) {
40695             // already added..
40696             return;
40697         }
40698         this.childForms.push(form);
40699         var n = '';
40700         Roo.each(form.allItems, function (fe) {
40701             
40702             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
40703             if (this.findField(n)) { // already added..
40704                 return;
40705             }
40706             var add = new Roo.form.Hidden({
40707                 name : n
40708             });
40709             add.render(this.el);
40710             
40711             this.add( add );
40712         }, this);
40713         
40714     },
40715     /**
40716      * Mark fields in this form invalid in bulk.
40717      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
40718      * @return {BasicForm} this
40719      */
40720     markInvalid : function(errors){
40721         if(errors instanceof Array){
40722             for(var i = 0, len = errors.length; i < len; i++){
40723                 var fieldError = errors[i];
40724                 var f = this.findField(fieldError.id);
40725                 if(f){
40726                     f.markInvalid(fieldError.msg);
40727                 }
40728             }
40729         }else{
40730             var field, id;
40731             for(id in errors){
40732                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
40733                     field.markInvalid(errors[id]);
40734                 }
40735             }
40736         }
40737         Roo.each(this.childForms || [], function (f) {
40738             f.markInvalid(errors);
40739         });
40740         
40741         return this;
40742     },
40743
40744     /**
40745      * Set values for fields in this form in bulk.
40746      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
40747      * @return {BasicForm} this
40748      */
40749     setValues : function(values){
40750         if(values instanceof Array){ // array of objects
40751             for(var i = 0, len = values.length; i < len; i++){
40752                 var v = values[i];
40753                 var f = this.findField(v.id);
40754                 if(f){
40755                     f.setValue(v.value);
40756                     if(this.trackResetOnLoad){
40757                         f.originalValue = f.getValue();
40758                     }
40759                 }
40760             }
40761         }else{ // object hash
40762             var field, id;
40763             for(id in values){
40764                 if(typeof values[id] != 'function' && (field = this.findField(id))){
40765                     
40766                     if (field.setFromData && 
40767                         field.valueField && 
40768                         field.displayField &&
40769                         // combos' with local stores can 
40770                         // be queried via setValue()
40771                         // to set their value..
40772                         (field.store && !field.store.isLocal)
40773                         ) {
40774                         // it's a combo
40775                         var sd = { };
40776                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
40777                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
40778                         field.setFromData(sd);
40779                         
40780                     } else {
40781                         field.setValue(values[id]);
40782                     }
40783                     
40784                     
40785                     if(this.trackResetOnLoad){
40786                         field.originalValue = field.getValue();
40787                     }
40788                 }
40789             }
40790         }
40791          
40792         Roo.each(this.childForms || [], function (f) {
40793             f.setValues(values);
40794         });
40795                 
40796         return this;
40797     },
40798
40799     /**
40800      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
40801      * they are returned as an array.
40802      * @param {Boolean} asString
40803      * @return {Object}
40804      */
40805     getValues : function(asString){
40806         if (this.childForms) {
40807             // copy values from the child forms
40808             Roo.each(this.childForms, function (f) {
40809                 this.setValues(f.getValues());
40810             }, this);
40811         }
40812         
40813         
40814         
40815         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
40816         if(asString === true){
40817             return fs;
40818         }
40819         return Roo.urlDecode(fs);
40820     },
40821     
40822     /**
40823      * Returns the fields in this form as an object with key/value pairs. 
40824      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
40825      * @return {Object}
40826      */
40827     getFieldValues : function()
40828     {
40829         if (this.childForms) {
40830             // copy values from the child forms
40831             Roo.each(this.childForms, function (f) {
40832                 this.setValues(f.getValues());
40833             }, this);
40834         }
40835         
40836         var ret = {};
40837         this.items.each(function(f){
40838             if (!f.getName()) {
40839                 return;
40840             }
40841             var v = f.getValue();
40842             if ((typeof(v) == 'object') && f.getRawValue) {
40843                 v = f.getRawValue() ; // dates..
40844             }
40845             ret[f.getName()] = v;
40846         });
40847         
40848         return ret;
40849     },
40850
40851     /**
40852      * Clears all invalid messages in this form.
40853      * @return {BasicForm} this
40854      */
40855     clearInvalid : function(){
40856         this.items.each(function(f){
40857            f.clearInvalid();
40858         });
40859         
40860         Roo.each(this.childForms || [], function (f) {
40861             f.clearInvalid();
40862         });
40863         
40864         
40865         return this;
40866     },
40867
40868     /**
40869      * Resets this form.
40870      * @return {BasicForm} this
40871      */
40872     reset : function(){
40873         this.items.each(function(f){
40874             f.reset();
40875         });
40876         
40877         Roo.each(this.childForms || [], function (f) {
40878             f.reset();
40879         });
40880        
40881         
40882         return this;
40883     },
40884
40885     /**
40886      * Add Roo.form components to this form.
40887      * @param {Field} field1
40888      * @param {Field} field2 (optional)
40889      * @param {Field} etc (optional)
40890      * @return {BasicForm} this
40891      */
40892     add : function(){
40893         this.items.addAll(Array.prototype.slice.call(arguments, 0));
40894         return this;
40895     },
40896
40897
40898     /**
40899      * Removes a field from the items collection (does NOT remove its markup).
40900      * @param {Field} field
40901      * @return {BasicForm} this
40902      */
40903     remove : function(field){
40904         this.items.remove(field);
40905         return this;
40906     },
40907
40908     /**
40909      * Looks at the fields in this form, checks them for an id attribute,
40910      * and calls applyTo on the existing dom element with that id.
40911      * @return {BasicForm} this
40912      */
40913     render : function(){
40914         this.items.each(function(f){
40915             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
40916                 f.applyTo(f.id);
40917             }
40918         });
40919         return this;
40920     },
40921
40922     /**
40923      * Calls {@link Ext#apply} for all fields in this form with the passed object.
40924      * @param {Object} values
40925      * @return {BasicForm} this
40926      */
40927     applyToFields : function(o){
40928         this.items.each(function(f){
40929            Roo.apply(f, o);
40930         });
40931         return this;
40932     },
40933
40934     /**
40935      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
40936      * @param {Object} values
40937      * @return {BasicForm} this
40938      */
40939     applyIfToFields : function(o){
40940         this.items.each(function(f){
40941            Roo.applyIf(f, o);
40942         });
40943         return this;
40944     }
40945 });
40946
40947 // back compat
40948 Roo.BasicForm = Roo.form.BasicForm;/*
40949  * Based on:
40950  * Ext JS Library 1.1.1
40951  * Copyright(c) 2006-2007, Ext JS, LLC.
40952  *
40953  * Originally Released Under LGPL - original licence link has changed is not relivant.
40954  *
40955  * Fork - LGPL
40956  * <script type="text/javascript">
40957  */
40958
40959 /**
40960  * @class Roo.form.Form
40961  * @extends Roo.form.BasicForm
40962  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
40963  * @constructor
40964  * @param {Object} config Configuration options
40965  */
40966 Roo.form.Form = function(config){
40967     var xitems =  [];
40968     if (config.items) {
40969         xitems = config.items;
40970         delete config.items;
40971     }
40972    
40973     
40974     Roo.form.Form.superclass.constructor.call(this, null, config);
40975     this.url = this.url || this.action;
40976     if(!this.root){
40977         this.root = new Roo.form.Layout(Roo.applyIf({
40978             id: Roo.id()
40979         }, config));
40980     }
40981     this.active = this.root;
40982     /**
40983      * Array of all the buttons that have been added to this form via {@link addButton}
40984      * @type Array
40985      */
40986     this.buttons = [];
40987     this.allItems = [];
40988     this.addEvents({
40989         /**
40990          * @event clientvalidation
40991          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
40992          * @param {Form} this
40993          * @param {Boolean} valid true if the form has passed client-side validation
40994          */
40995         clientvalidation: true,
40996         /**
40997          * @event rendered
40998          * Fires when the form is rendered
40999          * @param {Roo.form.Form} form
41000          */
41001         rendered : true
41002     });
41003     
41004     if (this.progressUrl) {
41005             // push a hidden field onto the list of fields..
41006             this.addxtype( {
41007                     xns: Roo.form, 
41008                     xtype : 'Hidden', 
41009                     name : 'UPLOAD_IDENTIFIER' 
41010             });
41011         }
41012         
41013     
41014     Roo.each(xitems, this.addxtype, this);
41015     
41016     
41017     
41018 };
41019
41020 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41021     /**
41022      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41023      */
41024     /**
41025      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41026      */
41027     /**
41028      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41029      */
41030     buttonAlign:'center',
41031
41032     /**
41033      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41034      */
41035     minButtonWidth:75,
41036
41037     /**
41038      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41039      * This property cascades to child containers if not set.
41040      */
41041     labelAlign:'left',
41042
41043     /**
41044      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41045      * fires a looping event with that state. This is required to bind buttons to the valid
41046      * state using the config value formBind:true on the button.
41047      */
41048     monitorValid : false,
41049
41050     /**
41051      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41052      */
41053     monitorPoll : 200,
41054     
41055     /**
41056      * @cfg {String} progressUrl - Url to return progress data 
41057      */
41058     
41059     progressUrl : false,
41060   
41061     /**
41062      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41063      * fields are added and the column is closed. If no fields are passed the column remains open
41064      * until end() is called.
41065      * @param {Object} config The config to pass to the column
41066      * @param {Field} field1 (optional)
41067      * @param {Field} field2 (optional)
41068      * @param {Field} etc (optional)
41069      * @return Column The column container object
41070      */
41071     column : function(c){
41072         var col = new Roo.form.Column(c);
41073         this.start(col);
41074         if(arguments.length > 1){ // duplicate code required because of Opera
41075             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41076             this.end();
41077         }
41078         return col;
41079     },
41080
41081     /**
41082      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41083      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41084      * until end() is called.
41085      * @param {Object} config The config to pass to the fieldset
41086      * @param {Field} field1 (optional)
41087      * @param {Field} field2 (optional)
41088      * @param {Field} etc (optional)
41089      * @return FieldSet The fieldset container object
41090      */
41091     fieldset : function(c){
41092         var fs = new Roo.form.FieldSet(c);
41093         this.start(fs);
41094         if(arguments.length > 1){ // duplicate code required because of Opera
41095             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41096             this.end();
41097         }
41098         return fs;
41099     },
41100
41101     /**
41102      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41103      * fields are added and the container is closed. If no fields are passed the container remains open
41104      * until end() is called.
41105      * @param {Object} config The config to pass to the Layout
41106      * @param {Field} field1 (optional)
41107      * @param {Field} field2 (optional)
41108      * @param {Field} etc (optional)
41109      * @return Layout The container object
41110      */
41111     container : function(c){
41112         var l = new Roo.form.Layout(c);
41113         this.start(l);
41114         if(arguments.length > 1){ // duplicate code required because of Opera
41115             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41116             this.end();
41117         }
41118         return l;
41119     },
41120
41121     /**
41122      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41123      * @param {Object} container A Roo.form.Layout or subclass of Layout
41124      * @return {Form} this
41125      */
41126     start : function(c){
41127         // cascade label info
41128         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41129         this.active.stack.push(c);
41130         c.ownerCt = this.active;
41131         this.active = c;
41132         return this;
41133     },
41134
41135     /**
41136      * Closes the current open container
41137      * @return {Form} this
41138      */
41139     end : function(){
41140         if(this.active == this.root){
41141             return this;
41142         }
41143         this.active = this.active.ownerCt;
41144         return this;
41145     },
41146
41147     /**
41148      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41149      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41150      * as the label of the field.
41151      * @param {Field} field1
41152      * @param {Field} field2 (optional)
41153      * @param {Field} etc. (optional)
41154      * @return {Form} this
41155      */
41156     add : function(){
41157         this.active.stack.push.apply(this.active.stack, arguments);
41158         this.allItems.push.apply(this.allItems,arguments);
41159         var r = [];
41160         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41161             if(a[i].isFormField){
41162                 r.push(a[i]);
41163             }
41164         }
41165         if(r.length > 0){
41166             Roo.form.Form.superclass.add.apply(this, r);
41167         }
41168         return this;
41169     },
41170     
41171
41172     
41173     
41174     
41175      /**
41176      * Find any element that has been added to a form, using it's ID or name
41177      * This can include framesets, columns etc. along with regular fields..
41178      * @param {String} id - id or name to find.
41179      
41180      * @return {Element} e - or false if nothing found.
41181      */
41182     findbyId : function(id)
41183     {
41184         var ret = false;
41185         if (!id) {
41186             return ret;
41187         }
41188         Ext.each(this.allItems, function(f){
41189             if (f.id == id || f.name == id ){
41190                 ret = f;
41191                 return false;
41192             }
41193         });
41194         return ret;
41195     },
41196
41197     
41198     
41199     /**
41200      * Render this form into the passed container. This should only be called once!
41201      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41202      * @return {Form} this
41203      */
41204     render : function(ct)
41205     {
41206         
41207         
41208         
41209         ct = Roo.get(ct);
41210         var o = this.autoCreate || {
41211             tag: 'form',
41212             method : this.method || 'POST',
41213             id : this.id || Roo.id()
41214         };
41215         this.initEl(ct.createChild(o));
41216
41217         this.root.render(this.el);
41218         
41219        
41220              
41221         this.items.each(function(f){
41222             f.render('x-form-el-'+f.id);
41223         });
41224
41225         if(this.buttons.length > 0){
41226             // tables are required to maintain order and for correct IE layout
41227             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41228                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41229                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41230             }}, null, true);
41231             var tr = tb.getElementsByTagName('tr')[0];
41232             for(var i = 0, len = this.buttons.length; i < len; i++) {
41233                 var b = this.buttons[i];
41234                 var td = document.createElement('td');
41235                 td.className = 'x-form-btn-td';
41236                 b.render(tr.appendChild(td));
41237             }
41238         }
41239         if(this.monitorValid){ // initialize after render
41240             this.startMonitoring();
41241         }
41242         this.fireEvent('rendered', this);
41243         return this;
41244     },
41245
41246     /**
41247      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41248      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41249      * object or a valid Roo.DomHelper element config
41250      * @param {Function} handler The function called when the button is clicked
41251      * @param {Object} scope (optional) The scope of the handler function
41252      * @return {Roo.Button}
41253      */
41254     addButton : function(config, handler, scope){
41255         var bc = {
41256             handler: handler,
41257             scope: scope,
41258             minWidth: this.minButtonWidth,
41259             hideParent:true
41260         };
41261         if(typeof config == "string"){
41262             bc.text = config;
41263         }else{
41264             Roo.apply(bc, config);
41265         }
41266         var btn = new Roo.Button(null, bc);
41267         this.buttons.push(btn);
41268         return btn;
41269     },
41270
41271      /**
41272      * Adds a series of form elements (using the xtype property as the factory method.
41273      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41274      * @param {Object} config 
41275      */
41276     
41277     addxtype : function()
41278     {
41279         var ar = Array.prototype.slice.call(arguments, 0);
41280         var ret = false;
41281         for(var i = 0; i < ar.length; i++) {
41282             if (!ar[i]) {
41283                 continue; // skip -- if this happends something invalid got sent, we 
41284                 // should ignore it, as basically that interface element will not show up
41285                 // and that should be pretty obvious!!
41286             }
41287             
41288             if (Roo.form[ar[i].xtype]) {
41289                 ar[i].form = this;
41290                 var fe = Roo.factory(ar[i], Roo.form);
41291                 if (!ret) {
41292                     ret = fe;
41293                 }
41294                 fe.form = this;
41295                 if (fe.store) {
41296                     fe.store.form = this;
41297                 }
41298                 if (fe.isLayout) {  
41299                          
41300                     this.start(fe);
41301                     this.allItems.push(fe);
41302                     if (fe.items && fe.addxtype) {
41303                         fe.addxtype.apply(fe, fe.items);
41304                         delete fe.items;
41305                     }
41306                      this.end();
41307                     continue;
41308                 }
41309                 
41310                 
41311                  
41312                 this.add(fe);
41313               //  console.log('adding ' + ar[i].xtype);
41314             }
41315             if (ar[i].xtype == 'Button') {  
41316                 //console.log('adding button');
41317                 //console.log(ar[i]);
41318                 this.addButton(ar[i]);
41319                 this.allItems.push(fe);
41320                 continue;
41321             }
41322             
41323             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41324                 alert('end is not supported on xtype any more, use items');
41325             //    this.end();
41326             //    //console.log('adding end');
41327             }
41328             
41329         }
41330         return ret;
41331     },
41332     
41333     /**
41334      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41335      * option "monitorValid"
41336      */
41337     startMonitoring : function(){
41338         if(!this.bound){
41339             this.bound = true;
41340             Roo.TaskMgr.start({
41341                 run : this.bindHandler,
41342                 interval : this.monitorPoll || 200,
41343                 scope: this
41344             });
41345         }
41346     },
41347
41348     /**
41349      * Stops monitoring of the valid state of this form
41350      */
41351     stopMonitoring : function(){
41352         this.bound = false;
41353     },
41354
41355     // private
41356     bindHandler : function(){
41357         if(!this.bound){
41358             return false; // stops binding
41359         }
41360         var valid = true;
41361         this.items.each(function(f){
41362             if(!f.isValid(true)){
41363                 valid = false;
41364                 return false;
41365             }
41366         });
41367         for(var i = 0, len = this.buttons.length; i < len; i++){
41368             var btn = this.buttons[i];
41369             if(btn.formBind === true && btn.disabled === valid){
41370                 btn.setDisabled(!valid);
41371             }
41372         }
41373         this.fireEvent('clientvalidation', this, valid);
41374     }
41375     
41376     
41377     
41378     
41379     
41380     
41381     
41382     
41383 });
41384
41385
41386 // back compat
41387 Roo.Form = Roo.form.Form;
41388 /*
41389  * Based on:
41390  * Ext JS Library 1.1.1
41391  * Copyright(c) 2006-2007, Ext JS, LLC.
41392  *
41393  * Originally Released Under LGPL - original licence link has changed is not relivant.
41394  *
41395  * Fork - LGPL
41396  * <script type="text/javascript">
41397  */
41398  
41399  /**
41400  * @class Roo.form.Action
41401  * Internal Class used to handle form actions
41402  * @constructor
41403  * @param {Roo.form.BasicForm} el The form element or its id
41404  * @param {Object} config Configuration options
41405  */
41406  
41407  
41408 // define the action interface
41409 Roo.form.Action = function(form, options){
41410     this.form = form;
41411     this.options = options || {};
41412 };
41413 /**
41414  * Client Validation Failed
41415  * @const 
41416  */
41417 Roo.form.Action.CLIENT_INVALID = 'client';
41418 /**
41419  * Server Validation Failed
41420  * @const 
41421  */
41422  Roo.form.Action.SERVER_INVALID = 'server';
41423  /**
41424  * Connect to Server Failed
41425  * @const 
41426  */
41427 Roo.form.Action.CONNECT_FAILURE = 'connect';
41428 /**
41429  * Reading Data from Server Failed
41430  * @const 
41431  */
41432 Roo.form.Action.LOAD_FAILURE = 'load';
41433
41434 Roo.form.Action.prototype = {
41435     type : 'default',
41436     failureType : undefined,
41437     response : undefined,
41438     result : undefined,
41439
41440     // interface method
41441     run : function(options){
41442
41443     },
41444
41445     // interface method
41446     success : function(response){
41447
41448     },
41449
41450     // interface method
41451     handleResponse : function(response){
41452
41453     },
41454
41455     // default connection failure
41456     failure : function(response){
41457         
41458         this.response = response;
41459         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41460         this.form.afterAction(this, false);
41461     },
41462
41463     processResponse : function(response){
41464         this.response = response;
41465         if(!response.responseText){
41466             return true;
41467         }
41468         this.result = this.handleResponse(response);
41469         return this.result;
41470     },
41471
41472     // utility functions used internally
41473     getUrl : function(appendParams){
41474         var url = this.options.url || this.form.url || this.form.el.dom.action;
41475         if(appendParams){
41476             var p = this.getParams();
41477             if(p){
41478                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
41479             }
41480         }
41481         return url;
41482     },
41483
41484     getMethod : function(){
41485         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
41486     },
41487
41488     getParams : function(){
41489         var bp = this.form.baseParams;
41490         var p = this.options.params;
41491         if(p){
41492             if(typeof p == "object"){
41493                 p = Roo.urlEncode(Roo.applyIf(p, bp));
41494             }else if(typeof p == 'string' && bp){
41495                 p += '&' + Roo.urlEncode(bp);
41496             }
41497         }else if(bp){
41498             p = Roo.urlEncode(bp);
41499         }
41500         return p;
41501     },
41502
41503     createCallback : function(){
41504         return {
41505             success: this.success,
41506             failure: this.failure,
41507             scope: this,
41508             timeout: (this.form.timeout*1000),
41509             upload: this.form.fileUpload ? this.success : undefined
41510         };
41511     }
41512 };
41513
41514 Roo.form.Action.Submit = function(form, options){
41515     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
41516 };
41517
41518 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
41519     type : 'submit',
41520
41521     haveProgress : false,
41522     uploadComplete : false,
41523     
41524     // uploadProgress indicator.
41525     uploadProgress : function()
41526     {
41527         if (!this.form.progressUrl) {
41528             return;
41529         }
41530         
41531         if (!this.haveProgress) {
41532             Roo.MessageBox.progress("Uploading", "Uploading");
41533         }
41534         if (this.uploadComplete) {
41535            Roo.MessageBox.hide();
41536            return;
41537         }
41538         
41539         this.haveProgress = true;
41540    
41541         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
41542         
41543         var c = new Roo.data.Connection();
41544         c.request({
41545             url : this.form.progressUrl,
41546             params: {
41547                 id : uid
41548             },
41549             method: 'GET',
41550             success : function(req){
41551                //console.log(data);
41552                 var rdata = false;
41553                 var edata;
41554                 try  {
41555                    rdata = Roo.decode(req.responseText)
41556                 } catch (e) {
41557                     Roo.log("Invalid data from server..");
41558                     Roo.log(edata);
41559                     return;
41560                 }
41561                 if (!rdata || !rdata.success) {
41562                     Roo.log(rdata);
41563                     return;
41564                 }
41565                 var data = rdata.data;
41566                 
41567                 if (this.uploadComplete) {
41568                    Roo.MessageBox.hide();
41569                    return;
41570                 }
41571                    
41572                 if (data){
41573                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
41574                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
41575                     );
41576                 }
41577                 this.uploadProgress.defer(2000,this);
41578             },
41579        
41580             failure: function(data) {
41581                 Roo.log('progress url failed ');
41582                 Roo.log(data);
41583             },
41584             scope : this
41585         });
41586            
41587     },
41588     
41589     
41590     run : function()
41591     {
41592         // run get Values on the form, so it syncs any secondary forms.
41593         this.form.getValues();
41594         
41595         var o = this.options;
41596         var method = this.getMethod();
41597         var isPost = method == 'POST';
41598         if(o.clientValidation === false || this.form.isValid()){
41599             
41600             if (this.form.progressUrl) {
41601                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
41602                     (new Date() * 1) + '' + Math.random());
41603                     
41604             } 
41605             
41606             
41607             Roo.Ajax.request(Roo.apply(this.createCallback(), {
41608                 form:this.form.el.dom,
41609                 url:this.getUrl(!isPost),
41610                 method: method,
41611                 params:isPost ? this.getParams() : null,
41612                 isUpload: this.form.fileUpload
41613             }));
41614             
41615             this.uploadProgress();
41616
41617         }else if (o.clientValidation !== false){ // client validation failed
41618             this.failureType = Roo.form.Action.CLIENT_INVALID;
41619             this.form.afterAction(this, false);
41620         }
41621     },
41622
41623     success : function(response)
41624     {
41625         this.uploadComplete= true;
41626         if (this.haveProgress) {
41627             Roo.MessageBox.hide();
41628         }
41629         
41630         
41631         var result = this.processResponse(response);
41632         if(result === true || result.success){
41633             this.form.afterAction(this, true);
41634             return;
41635         }
41636         if(result.errors){
41637             this.form.markInvalid(result.errors);
41638             this.failureType = Roo.form.Action.SERVER_INVALID;
41639         }
41640         this.form.afterAction(this, false);
41641     },
41642     failure : function(response)
41643     {
41644         this.uploadComplete= true;
41645         if (this.haveProgress) {
41646             Roo.MessageBox.hide();
41647         }
41648         
41649         
41650         this.response = response;
41651         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41652         this.form.afterAction(this, false);
41653     },
41654     
41655     handleResponse : function(response){
41656         if(this.form.errorReader){
41657             var rs = this.form.errorReader.read(response);
41658             var errors = [];
41659             if(rs.records){
41660                 for(var i = 0, len = rs.records.length; i < len; i++) {
41661                     var r = rs.records[i];
41662                     errors[i] = r.data;
41663                 }
41664             }
41665             if(errors.length < 1){
41666                 errors = null;
41667             }
41668             return {
41669                 success : rs.success,
41670                 errors : errors
41671             };
41672         }
41673         var ret = false;
41674         try {
41675             ret = Roo.decode(response.responseText);
41676         } catch (e) {
41677             ret = {
41678                 success: false,
41679                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
41680                 errors : []
41681             };
41682         }
41683         return ret;
41684         
41685     }
41686 });
41687
41688
41689 Roo.form.Action.Load = function(form, options){
41690     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
41691     this.reader = this.form.reader;
41692 };
41693
41694 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
41695     type : 'load',
41696
41697     run : function(){
41698         
41699         Roo.Ajax.request(Roo.apply(
41700                 this.createCallback(), {
41701                     method:this.getMethod(),
41702                     url:this.getUrl(false),
41703                     params:this.getParams()
41704         }));
41705     },
41706
41707     success : function(response){
41708         
41709         var result = this.processResponse(response);
41710         if(result === true || !result.success || !result.data){
41711             this.failureType = Roo.form.Action.LOAD_FAILURE;
41712             this.form.afterAction(this, false);
41713             return;
41714         }
41715         this.form.clearInvalid();
41716         this.form.setValues(result.data);
41717         this.form.afterAction(this, true);
41718     },
41719
41720     handleResponse : function(response){
41721         if(this.form.reader){
41722             var rs = this.form.reader.read(response);
41723             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
41724             return {
41725                 success : rs.success,
41726                 data : data
41727             };
41728         }
41729         return Roo.decode(response.responseText);
41730     }
41731 });
41732
41733 Roo.form.Action.ACTION_TYPES = {
41734     'load' : Roo.form.Action.Load,
41735     'submit' : Roo.form.Action.Submit
41736 };/*
41737  * Based on:
41738  * Ext JS Library 1.1.1
41739  * Copyright(c) 2006-2007, Ext JS, LLC.
41740  *
41741  * Originally Released Under LGPL - original licence link has changed is not relivant.
41742  *
41743  * Fork - LGPL
41744  * <script type="text/javascript">
41745  */
41746  
41747 /**
41748  * @class Roo.form.Layout
41749  * @extends Roo.Component
41750  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
41751  * @constructor
41752  * @param {Object} config Configuration options
41753  */
41754 Roo.form.Layout = function(config){
41755     var xitems = [];
41756     if (config.items) {
41757         xitems = config.items;
41758         delete config.items;
41759     }
41760     Roo.form.Layout.superclass.constructor.call(this, config);
41761     this.stack = [];
41762     Roo.each(xitems, this.addxtype, this);
41763      
41764 };
41765
41766 Roo.extend(Roo.form.Layout, Roo.Component, {
41767     /**
41768      * @cfg {String/Object} autoCreate
41769      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
41770      */
41771     /**
41772      * @cfg {String/Object/Function} style
41773      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
41774      * a function which returns such a specification.
41775      */
41776     /**
41777      * @cfg {String} labelAlign
41778      * Valid values are "left," "top" and "right" (defaults to "left")
41779      */
41780     /**
41781      * @cfg {Number} labelWidth
41782      * Fixed width in pixels of all field labels (defaults to undefined)
41783      */
41784     /**
41785      * @cfg {Boolean} clear
41786      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
41787      */
41788     clear : true,
41789     /**
41790      * @cfg {String} labelSeparator
41791      * The separator to use after field labels (defaults to ':')
41792      */
41793     labelSeparator : ':',
41794     /**
41795      * @cfg {Boolean} hideLabels
41796      * True to suppress the display of field labels in this layout (defaults to false)
41797      */
41798     hideLabels : false,
41799
41800     // private
41801     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
41802     
41803     isLayout : true,
41804     
41805     // private
41806     onRender : function(ct, position){
41807         if(this.el){ // from markup
41808             this.el = Roo.get(this.el);
41809         }else {  // generate
41810             var cfg = this.getAutoCreate();
41811             this.el = ct.createChild(cfg, position);
41812         }
41813         if(this.style){
41814             this.el.applyStyles(this.style);
41815         }
41816         if(this.labelAlign){
41817             this.el.addClass('x-form-label-'+this.labelAlign);
41818         }
41819         if(this.hideLabels){
41820             this.labelStyle = "display:none";
41821             this.elementStyle = "padding-left:0;";
41822         }else{
41823             if(typeof this.labelWidth == 'number'){
41824                 this.labelStyle = "width:"+this.labelWidth+"px;";
41825                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
41826             }
41827             if(this.labelAlign == 'top'){
41828                 this.labelStyle = "width:auto;";
41829                 this.elementStyle = "padding-left:0;";
41830             }
41831         }
41832         var stack = this.stack;
41833         var slen = stack.length;
41834         if(slen > 0){
41835             if(!this.fieldTpl){
41836                 var t = new Roo.Template(
41837                     '<div class="x-form-item {5}">',
41838                         '<label for="{0}" style="{2}">{1}{4}</label>',
41839                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41840                         '</div>',
41841                     '</div><div class="x-form-clear-left"></div>'
41842                 );
41843                 t.disableFormats = true;
41844                 t.compile();
41845                 Roo.form.Layout.prototype.fieldTpl = t;
41846             }
41847             for(var i = 0; i < slen; i++) {
41848                 if(stack[i].isFormField){
41849                     this.renderField(stack[i]);
41850                 }else{
41851                     this.renderComponent(stack[i]);
41852                 }
41853             }
41854         }
41855         if(this.clear){
41856             this.el.createChild({cls:'x-form-clear'});
41857         }
41858     },
41859
41860     // private
41861     renderField : function(f){
41862         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
41863                f.id, //0
41864                f.fieldLabel, //1
41865                f.labelStyle||this.labelStyle||'', //2
41866                this.elementStyle||'', //3
41867                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
41868                f.itemCls||this.itemCls||''  //5
41869        ], true).getPrevSibling());
41870     },
41871
41872     // private
41873     renderComponent : function(c){
41874         c.render(c.isLayout ? this.el : this.el.createChild());    
41875     },
41876     /**
41877      * Adds a object form elements (using the xtype property as the factory method.)
41878      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
41879      * @param {Object} config 
41880      */
41881     addxtype : function(o)
41882     {
41883         // create the lement.
41884         o.form = this.form;
41885         var fe = Roo.factory(o, Roo.form);
41886         this.form.allItems.push(fe);
41887         this.stack.push(fe);
41888         
41889         if (fe.isFormField) {
41890             this.form.items.add(fe);
41891         }
41892          
41893         return fe;
41894     }
41895 });
41896
41897 /**
41898  * @class Roo.form.Column
41899  * @extends Roo.form.Layout
41900  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
41901  * @constructor
41902  * @param {Object} config Configuration options
41903  */
41904 Roo.form.Column = function(config){
41905     Roo.form.Column.superclass.constructor.call(this, config);
41906 };
41907
41908 Roo.extend(Roo.form.Column, Roo.form.Layout, {
41909     /**
41910      * @cfg {Number/String} width
41911      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41912      */
41913     /**
41914      * @cfg {String/Object} autoCreate
41915      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
41916      */
41917
41918     // private
41919     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
41920
41921     // private
41922     onRender : function(ct, position){
41923         Roo.form.Column.superclass.onRender.call(this, ct, position);
41924         if(this.width){
41925             this.el.setWidth(this.width);
41926         }
41927     }
41928 });
41929
41930
41931 /**
41932  * @class Roo.form.Row
41933  * @extends Roo.form.Layout
41934  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
41935  * @constructor
41936  * @param {Object} config Configuration options
41937  */
41938
41939  
41940 Roo.form.Row = function(config){
41941     Roo.form.Row.superclass.constructor.call(this, config);
41942 };
41943  
41944 Roo.extend(Roo.form.Row, Roo.form.Layout, {
41945       /**
41946      * @cfg {Number/String} width
41947      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41948      */
41949     /**
41950      * @cfg {Number/String} height
41951      * The fixed height of the column in pixels or CSS value (defaults to "auto")
41952      */
41953     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
41954     
41955     padWidth : 20,
41956     // private
41957     onRender : function(ct, position){
41958         //console.log('row render');
41959         if(!this.rowTpl){
41960             var t = new Roo.Template(
41961                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
41962                     '<label for="{0}" style="{2}">{1}{4}</label>',
41963                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41964                     '</div>',
41965                 '</div>'
41966             );
41967             t.disableFormats = true;
41968             t.compile();
41969             Roo.form.Layout.prototype.rowTpl = t;
41970         }
41971         this.fieldTpl = this.rowTpl;
41972         
41973         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
41974         var labelWidth = 100;
41975         
41976         if ((this.labelAlign != 'top')) {
41977             if (typeof this.labelWidth == 'number') {
41978                 labelWidth = this.labelWidth
41979             }
41980             this.padWidth =  20 + labelWidth;
41981             
41982         }
41983         
41984         Roo.form.Column.superclass.onRender.call(this, ct, position);
41985         if(this.width){
41986             this.el.setWidth(this.width);
41987         }
41988         if(this.height){
41989             this.el.setHeight(this.height);
41990         }
41991     },
41992     
41993     // private
41994     renderField : function(f){
41995         f.fieldEl = this.fieldTpl.append(this.el, [
41996                f.id, f.fieldLabel,
41997                f.labelStyle||this.labelStyle||'',
41998                this.elementStyle||'',
41999                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42000                f.itemCls||this.itemCls||'',
42001                f.width ? f.width + this.padWidth : 160 + this.padWidth
42002        ],true);
42003     }
42004 });
42005  
42006
42007 /**
42008  * @class Roo.form.FieldSet
42009  * @extends Roo.form.Layout
42010  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42011  * @constructor
42012  * @param {Object} config Configuration options
42013  */
42014 Roo.form.FieldSet = function(config){
42015     Roo.form.FieldSet.superclass.constructor.call(this, config);
42016 };
42017
42018 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42019     /**
42020      * @cfg {String} legend
42021      * The text to display as the legend for the FieldSet (defaults to '')
42022      */
42023     /**
42024      * @cfg {String/Object} autoCreate
42025      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42026      */
42027
42028     // private
42029     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42030
42031     // private
42032     onRender : function(ct, position){
42033         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42034         if(this.legend){
42035             this.setLegend(this.legend);
42036         }
42037     },
42038
42039     // private
42040     setLegend : function(text){
42041         if(this.rendered){
42042             this.el.child('legend').update(text);
42043         }
42044     }
42045 });/*
42046  * Based on:
42047  * Ext JS Library 1.1.1
42048  * Copyright(c) 2006-2007, Ext JS, LLC.
42049  *
42050  * Originally Released Under LGPL - original licence link has changed is not relivant.
42051  *
42052  * Fork - LGPL
42053  * <script type="text/javascript">
42054  */
42055 /**
42056  * @class Roo.form.VTypes
42057  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42058  * @singleton
42059  */
42060 Roo.form.VTypes = function(){
42061     // closure these in so they are only created once.
42062     var alpha = /^[a-zA-Z_]+$/;
42063     var alphanum = /^[a-zA-Z0-9_]+$/;
42064     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42065     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42066
42067     // All these messages and functions are configurable
42068     return {
42069         /**
42070          * The function used to validate email addresses
42071          * @param {String} value The email address
42072          */
42073         'email' : function(v){
42074             return email.test(v);
42075         },
42076         /**
42077          * The error text to display when the email validation function returns false
42078          * @type String
42079          */
42080         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42081         /**
42082          * The keystroke filter mask to be applied on email input
42083          * @type RegExp
42084          */
42085         'emailMask' : /[a-z0-9_\.\-@]/i,
42086
42087         /**
42088          * The function used to validate URLs
42089          * @param {String} value The URL
42090          */
42091         'url' : function(v){
42092             return url.test(v);
42093         },
42094         /**
42095          * The error text to display when the url validation function returns false
42096          * @type String
42097          */
42098         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42099         
42100         /**
42101          * The function used to validate alpha values
42102          * @param {String} value The value
42103          */
42104         'alpha' : function(v){
42105             return alpha.test(v);
42106         },
42107         /**
42108          * The error text to display when the alpha validation function returns false
42109          * @type String
42110          */
42111         'alphaText' : 'This field should only contain letters and _',
42112         /**
42113          * The keystroke filter mask to be applied on alpha input
42114          * @type RegExp
42115          */
42116         'alphaMask' : /[a-z_]/i,
42117
42118         /**
42119          * The function used to validate alphanumeric values
42120          * @param {String} value The value
42121          */
42122         'alphanum' : function(v){
42123             return alphanum.test(v);
42124         },
42125         /**
42126          * The error text to display when the alphanumeric validation function returns false
42127          * @type String
42128          */
42129         'alphanumText' : 'This field should only contain letters, numbers and _',
42130         /**
42131          * The keystroke filter mask to be applied on alphanumeric input
42132          * @type RegExp
42133          */
42134         'alphanumMask' : /[a-z0-9_]/i
42135     };
42136 }();//<script type="text/javascript">
42137
42138 /**
42139  * @class Roo.form.FCKeditor
42140  * @extends Roo.form.TextArea
42141  * Wrapper around the FCKEditor http://www.fckeditor.net
42142  * @constructor
42143  * Creates a new FCKeditor
42144  * @param {Object} config Configuration options
42145  */
42146 Roo.form.FCKeditor = function(config){
42147     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42148     this.addEvents({
42149          /**
42150          * @event editorinit
42151          * Fired when the editor is initialized - you can add extra handlers here..
42152          * @param {FCKeditor} this
42153          * @param {Object} the FCK object.
42154          */
42155         editorinit : true
42156     });
42157     
42158     
42159 };
42160 Roo.form.FCKeditor.editors = { };
42161 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42162 {
42163     //defaultAutoCreate : {
42164     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42165     //},
42166     // private
42167     /**
42168      * @cfg {Object} fck options - see fck manual for details.
42169      */
42170     fckconfig : false,
42171     
42172     /**
42173      * @cfg {Object} fck toolbar set (Basic or Default)
42174      */
42175     toolbarSet : 'Basic',
42176     /**
42177      * @cfg {Object} fck BasePath
42178      */ 
42179     basePath : '/fckeditor/',
42180     
42181     
42182     frame : false,
42183     
42184     value : '',
42185     
42186    
42187     onRender : function(ct, position)
42188     {
42189         if(!this.el){
42190             this.defaultAutoCreate = {
42191                 tag: "textarea",
42192                 style:"width:300px;height:60px;",
42193                 autocomplete: "off"
42194             };
42195         }
42196         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42197         /*
42198         if(this.grow){
42199             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42200             if(this.preventScrollbars){
42201                 this.el.setStyle("overflow", "hidden");
42202             }
42203             this.el.setHeight(this.growMin);
42204         }
42205         */
42206         //console.log('onrender' + this.getId() );
42207         Roo.form.FCKeditor.editors[this.getId()] = this;
42208          
42209
42210         this.replaceTextarea() ;
42211         
42212     },
42213     
42214     getEditor : function() {
42215         return this.fckEditor;
42216     },
42217     /**
42218      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42219      * @param {Mixed} value The value to set
42220      */
42221     
42222     
42223     setValue : function(value)
42224     {
42225         //console.log('setValue: ' + value);
42226         
42227         if(typeof(value) == 'undefined') { // not sure why this is happending...
42228             return;
42229         }
42230         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42231         
42232         //if(!this.el || !this.getEditor()) {
42233         //    this.value = value;
42234             //this.setValue.defer(100,this,[value]);    
42235         //    return;
42236         //} 
42237         
42238         if(!this.getEditor()) {
42239             return;
42240         }
42241         
42242         this.getEditor().SetData(value);
42243         
42244         //
42245
42246     },
42247
42248     /**
42249      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42250      * @return {Mixed} value The field value
42251      */
42252     getValue : function()
42253     {
42254         
42255         if (this.frame && this.frame.dom.style.display == 'none') {
42256             return Roo.form.FCKeditor.superclass.getValue.call(this);
42257         }
42258         
42259         if(!this.el || !this.getEditor()) {
42260            
42261            // this.getValue.defer(100,this); 
42262             return this.value;
42263         }
42264        
42265         
42266         var value=this.getEditor().GetData();
42267         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42268         return Roo.form.FCKeditor.superclass.getValue.call(this);
42269         
42270
42271     },
42272
42273     /**
42274      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42275      * @return {Mixed} value The field value
42276      */
42277     getRawValue : function()
42278     {
42279         if (this.frame && this.frame.dom.style.display == 'none') {
42280             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42281         }
42282         
42283         if(!this.el || !this.getEditor()) {
42284             //this.getRawValue.defer(100,this); 
42285             return this.value;
42286             return;
42287         }
42288         
42289         
42290         
42291         var value=this.getEditor().GetData();
42292         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42293         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42294          
42295     },
42296     
42297     setSize : function(w,h) {
42298         
42299         
42300         
42301         //if (this.frame && this.frame.dom.style.display == 'none') {
42302         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42303         //    return;
42304         //}
42305         //if(!this.el || !this.getEditor()) {
42306         //    this.setSize.defer(100,this, [w,h]); 
42307         //    return;
42308         //}
42309         
42310         
42311         
42312         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42313         
42314         this.frame.dom.setAttribute('width', w);
42315         this.frame.dom.setAttribute('height', h);
42316         this.frame.setSize(w,h);
42317         
42318     },
42319     
42320     toggleSourceEdit : function(value) {
42321         
42322       
42323          
42324         this.el.dom.style.display = value ? '' : 'none';
42325         this.frame.dom.style.display = value ?  'none' : '';
42326         
42327     },
42328     
42329     
42330     focus: function(tag)
42331     {
42332         if (this.frame.dom.style.display == 'none') {
42333             return Roo.form.FCKeditor.superclass.focus.call(this);
42334         }
42335         if(!this.el || !this.getEditor()) {
42336             this.focus.defer(100,this, [tag]); 
42337             return;
42338         }
42339         
42340         
42341         
42342         
42343         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
42344         this.getEditor().Focus();
42345         if (tgs.length) {
42346             if (!this.getEditor().Selection.GetSelection()) {
42347                 this.focus.defer(100,this, [tag]); 
42348                 return;
42349             }
42350             
42351             
42352             var r = this.getEditor().EditorDocument.createRange();
42353             r.setStart(tgs[0],0);
42354             r.setEnd(tgs[0],0);
42355             this.getEditor().Selection.GetSelection().removeAllRanges();
42356             this.getEditor().Selection.GetSelection().addRange(r);
42357             this.getEditor().Focus();
42358         }
42359         
42360     },
42361     
42362     
42363     
42364     replaceTextarea : function()
42365     {
42366         if ( document.getElementById( this.getId() + '___Frame' ) )
42367             return ;
42368         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
42369         //{
42370             // We must check the elements firstly using the Id and then the name.
42371         var oTextarea = document.getElementById( this.getId() );
42372         
42373         var colElementsByName = document.getElementsByName( this.getId() ) ;
42374          
42375         oTextarea.style.display = 'none' ;
42376
42377         if ( oTextarea.tabIndex ) {            
42378             this.TabIndex = oTextarea.tabIndex ;
42379         }
42380         
42381         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
42382         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
42383         this.frame = Roo.get(this.getId() + '___Frame')
42384     },
42385     
42386     _getConfigHtml : function()
42387     {
42388         var sConfig = '' ;
42389
42390         for ( var o in this.fckconfig ) {
42391             sConfig += sConfig.length > 0  ? '&amp;' : '';
42392             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
42393         }
42394
42395         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
42396     },
42397     
42398     
42399     _getIFrameHtml : function()
42400     {
42401         var sFile = 'fckeditor.html' ;
42402         /* no idea what this is about..
42403         try
42404         {
42405             if ( (/fcksource=true/i).test( window.top.location.search ) )
42406                 sFile = 'fckeditor.original.html' ;
42407         }
42408         catch (e) { 
42409         */
42410
42411         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
42412         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
42413         
42414         
42415         var html = '<iframe id="' + this.getId() +
42416             '___Frame" src="' + sLink +
42417             '" width="' + this.width +
42418             '" height="' + this.height + '"' +
42419             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
42420             ' frameborder="0" scrolling="no"></iframe>' ;
42421
42422         return html ;
42423     },
42424     
42425     _insertHtmlBefore : function( html, element )
42426     {
42427         if ( element.insertAdjacentHTML )       {
42428             // IE
42429             element.insertAdjacentHTML( 'beforeBegin', html ) ;
42430         } else { // Gecko
42431             var oRange = document.createRange() ;
42432             oRange.setStartBefore( element ) ;
42433             var oFragment = oRange.createContextualFragment( html );
42434             element.parentNode.insertBefore( oFragment, element ) ;
42435         }
42436     }
42437     
42438     
42439   
42440     
42441     
42442     
42443     
42444
42445 });
42446
42447 //Roo.reg('fckeditor', Roo.form.FCKeditor);
42448
42449 function FCKeditor_OnComplete(editorInstance){
42450     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
42451     f.fckEditor = editorInstance;
42452     //console.log("loaded");
42453     f.fireEvent('editorinit', f, editorInstance);
42454
42455   
42456
42457  
42458
42459
42460
42461
42462
42463
42464
42465
42466
42467
42468
42469
42470
42471
42472
42473 //<script type="text/javascript">
42474 /**
42475  * @class Roo.form.GridField
42476  * @extends Roo.form.Field
42477  * Embed a grid (or editable grid into a form)
42478  * STATUS ALPHA
42479  * 
42480  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
42481  * it needs 
42482  * xgrid.store = Roo.data.Store
42483  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
42484  * xgrid.store.reader = Roo.data.JsonReader 
42485  * 
42486  * 
42487  * @constructor
42488  * Creates a new GridField
42489  * @param {Object} config Configuration options
42490  */
42491 Roo.form.GridField = function(config){
42492     Roo.form.GridField.superclass.constructor.call(this, config);
42493      
42494 };
42495
42496 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
42497     /**
42498      * @cfg {Number} width  - used to restrict width of grid..
42499      */
42500     width : 100,
42501     /**
42502      * @cfg {Number} height - used to restrict height of grid..
42503      */
42504     height : 50,
42505      /**
42506      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
42507          * 
42508          *}
42509      */
42510     xgrid : false, 
42511     /**
42512      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42513      * {tag: "input", type: "checkbox", autocomplete: "off"})
42514      */
42515    // defaultAutoCreate : { tag: 'div' },
42516     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42517     /**
42518      * @cfg {String} addTitle Text to include for adding a title.
42519      */
42520     addTitle : false,
42521     //
42522     onResize : function(){
42523         Roo.form.Field.superclass.onResize.apply(this, arguments);
42524     },
42525
42526     initEvents : function(){
42527         // Roo.form.Checkbox.superclass.initEvents.call(this);
42528         // has no events...
42529        
42530     },
42531
42532
42533     getResizeEl : function(){
42534         return this.wrap;
42535     },
42536
42537     getPositionEl : function(){
42538         return this.wrap;
42539     },
42540
42541     // private
42542     onRender : function(ct, position){
42543         
42544         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
42545         var style = this.style;
42546         delete this.style;
42547         
42548         Roo.form.GridField.superclass.onRender.call(this, ct, position);
42549         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
42550         this.viewEl = this.wrap.createChild({ tag: 'div' });
42551         if (style) {
42552             this.viewEl.applyStyles(style);
42553         }
42554         if (this.width) {
42555             this.viewEl.setWidth(this.width);
42556         }
42557         if (this.height) {
42558             this.viewEl.setHeight(this.height);
42559         }
42560         //if(this.inputValue !== undefined){
42561         //this.setValue(this.value);
42562         
42563         
42564         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
42565         
42566         
42567         this.grid.render();
42568         this.grid.getDataSource().on('remove', this.refreshValue, this);
42569         this.grid.getDataSource().on('update', this.refreshValue, this);
42570         this.grid.on('afteredit', this.refreshValue, this);
42571  
42572     },
42573      
42574     
42575     /**
42576      * Sets the value of the item. 
42577      * @param {String} either an object  or a string..
42578      */
42579     setValue : function(v){
42580         //this.value = v;
42581         v = v || []; // empty set..
42582         // this does not seem smart - it really only affects memoryproxy grids..
42583         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
42584             var ds = this.grid.getDataSource();
42585             // assumes a json reader..
42586             var data = {}
42587             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
42588             ds.loadData( data);
42589         }
42590         Roo.form.GridField.superclass.setValue.call(this, v);
42591         this.refreshValue();
42592         // should load data in the grid really....
42593     },
42594     
42595     // private
42596     refreshValue: function() {
42597          var val = [];
42598         this.grid.getDataSource().each(function(r) {
42599             val.push(r.data);
42600         });
42601         this.el.dom.value = Roo.encode(val);
42602     }
42603     
42604      
42605     
42606     
42607 });/*
42608  * Based on:
42609  * Ext JS Library 1.1.1
42610  * Copyright(c) 2006-2007, Ext JS, LLC.
42611  *
42612  * Originally Released Under LGPL - original licence link has changed is not relivant.
42613  *
42614  * Fork - LGPL
42615  * <script type="text/javascript">
42616  */
42617 /**
42618  * @class Roo.form.DisplayField
42619  * @extends Roo.form.Field
42620  * A generic Field to display non-editable data.
42621  * @constructor
42622  * Creates a new Display Field item.
42623  * @param {Object} config Configuration options
42624  */
42625 Roo.form.DisplayField = function(config){
42626     Roo.form.DisplayField.superclass.constructor.call(this, config);
42627     
42628 };
42629
42630 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
42631     inputType:      'hidden',
42632     allowBlank:     true,
42633     readOnly:         true,
42634     
42635  
42636     /**
42637      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42638      */
42639     focusClass : undefined,
42640     /**
42641      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42642      */
42643     fieldClass: 'x-form-field',
42644     
42645      /**
42646      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
42647      */
42648     valueRenderer: undefined,
42649     
42650     width: 100,
42651     /**
42652      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42653      * {tag: "input", type: "checkbox", autocomplete: "off"})
42654      */
42655      
42656  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42657
42658     onResize : function(){
42659         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
42660         
42661     },
42662
42663     initEvents : function(){
42664         // Roo.form.Checkbox.superclass.initEvents.call(this);
42665         // has no events...
42666        
42667     },
42668
42669
42670     getResizeEl : function(){
42671         return this.wrap;
42672     },
42673
42674     getPositionEl : function(){
42675         return this.wrap;
42676     },
42677
42678     // private
42679     onRender : function(ct, position){
42680         
42681         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
42682         //if(this.inputValue !== undefined){
42683         this.wrap = this.el.wrap();
42684         
42685         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
42686         
42687         if (this.bodyStyle) {
42688             this.viewEl.applyStyles(this.bodyStyle);
42689         }
42690         //this.viewEl.setStyle('padding', '2px');
42691         
42692         this.setValue(this.value);
42693         
42694     },
42695 /*
42696     // private
42697     initValue : Roo.emptyFn,
42698
42699   */
42700
42701         // private
42702     onClick : function(){
42703         
42704     },
42705
42706     /**
42707      * Sets the checked state of the checkbox.
42708      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
42709      */
42710     setValue : function(v){
42711         this.value = v;
42712         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
42713         // this might be called before we have a dom element..
42714         if (!this.viewEl) {
42715             return;
42716         }
42717         this.viewEl.dom.innerHTML = html;
42718         Roo.form.DisplayField.superclass.setValue.call(this, v);
42719
42720     }
42721 });/*
42722  * 
42723  * Licence- LGPL
42724  * 
42725  */
42726
42727 /**
42728  * @class Roo.form.DayPicker
42729  * @extends Roo.form.Field
42730  * A Day picker show [M] [T] [W] ....
42731  * @constructor
42732  * Creates a new Day Picker
42733  * @param {Object} config Configuration options
42734  */
42735 Roo.form.DayPicker= function(config){
42736     Roo.form.DayPicker.superclass.constructor.call(this, config);
42737      
42738 };
42739
42740 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
42741     /**
42742      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42743      */
42744     focusClass : undefined,
42745     /**
42746      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42747      */
42748     fieldClass: "x-form-field",
42749    
42750     /**
42751      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42752      * {tag: "input", type: "checkbox", autocomplete: "off"})
42753      */
42754     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42755     
42756    
42757     actionMode : 'viewEl', 
42758     //
42759     // private
42760  
42761     inputType : 'hidden',
42762     
42763      
42764     inputElement: false, // real input element?
42765     basedOn: false, // ????
42766     
42767     isFormField: true, // not sure where this is needed!!!!
42768
42769     onResize : function(){
42770         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42771         if(!this.boxLabel){
42772             this.el.alignTo(this.wrap, 'c-c');
42773         }
42774     },
42775
42776     initEvents : function(){
42777         Roo.form.Checkbox.superclass.initEvents.call(this);
42778         this.el.on("click", this.onClick,  this);
42779         this.el.on("change", this.onClick,  this);
42780     },
42781
42782
42783     getResizeEl : function(){
42784         return this.wrap;
42785     },
42786
42787     getPositionEl : function(){
42788         return this.wrap;
42789     },
42790
42791     
42792     // private
42793     onRender : function(ct, position){
42794         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42795        
42796         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
42797         
42798         var r1 = '<table><tr>';
42799         var r2 = '<tr class="x-form-daypick-icons">';
42800         for (var i=0; i < 7; i++) {
42801             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
42802             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
42803         }
42804         
42805         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
42806         viewEl.select('img').on('click', this.onClick, this);
42807         this.viewEl = viewEl;   
42808         
42809         
42810         // this will not work on Chrome!!!
42811         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42812         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42813         
42814         
42815           
42816
42817     },
42818
42819     // private
42820     initValue : Roo.emptyFn,
42821
42822     /**
42823      * Returns the checked state of the checkbox.
42824      * @return {Boolean} True if checked, else false
42825      */
42826     getValue : function(){
42827         return this.el.dom.value;
42828         
42829     },
42830
42831         // private
42832     onClick : function(e){ 
42833         //this.setChecked(!this.checked);
42834         Roo.get(e.target).toggleClass('x-menu-item-checked');
42835         this.refreshValue();
42836         //if(this.el.dom.checked != this.checked){
42837         //    this.setValue(this.el.dom.checked);
42838        // }
42839     },
42840     
42841     // private
42842     refreshValue : function()
42843     {
42844         var val = '';
42845         this.viewEl.select('img',true).each(function(e,i,n)  {
42846             val += e.is(".x-menu-item-checked") ? String(n) : '';
42847         });
42848         this.setValue(val, true);
42849     },
42850
42851     /**
42852      * Sets the checked state of the checkbox.
42853      * On is always based on a string comparison between inputValue and the param.
42854      * @param {Boolean/String} value - the value to set 
42855      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42856      */
42857     setValue : function(v,suppressEvent){
42858         if (!this.el.dom) {
42859             return;
42860         }
42861         var old = this.el.dom.value ;
42862         this.el.dom.value = v;
42863         if (suppressEvent) {
42864             return ;
42865         }
42866          
42867         // update display..
42868         this.viewEl.select('img',true).each(function(e,i,n)  {
42869             
42870             var on = e.is(".x-menu-item-checked");
42871             var newv = v.indexOf(String(n)) > -1;
42872             if (on != newv) {
42873                 e.toggleClass('x-menu-item-checked');
42874             }
42875             
42876         });
42877         
42878         
42879         this.fireEvent('change', this, v, old);
42880         
42881         
42882     },
42883    
42884     // handle setting of hidden value by some other method!!?!?
42885     setFromHidden: function()
42886     {
42887         if(!this.el){
42888             return;
42889         }
42890         //console.log("SET FROM HIDDEN");
42891         //alert('setFrom hidden');
42892         this.setValue(this.el.dom.value);
42893     },
42894     
42895     onDestroy : function()
42896     {
42897         if(this.viewEl){
42898             Roo.get(this.viewEl).remove();
42899         }
42900          
42901         Roo.form.DayPicker.superclass.onDestroy.call(this);
42902     }
42903
42904 });//<script type="text/javasscript">
42905  
42906
42907 /**
42908  * @class Roo.DDView
42909  * A DnD enabled version of Roo.View.
42910  * @param {Element/String} container The Element in which to create the View.
42911  * @param {String} tpl The template string used to create the markup for each element of the View
42912  * @param {Object} config The configuration properties. These include all the config options of
42913  * {@link Roo.View} plus some specific to this class.<br>
42914  * <p>
42915  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
42916  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
42917  * <p>
42918  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
42919 .x-view-drag-insert-above {
42920         border-top:1px dotted #3366cc;
42921 }
42922 .x-view-drag-insert-below {
42923         border-bottom:1px dotted #3366cc;
42924 }
42925 </code></pre>
42926  * 
42927  */
42928  
42929 Roo.DDView = function(container, tpl, config) {
42930     Roo.DDView.superclass.constructor.apply(this, arguments);
42931     this.getEl().setStyle("outline", "0px none");
42932     this.getEl().unselectable();
42933     if (this.dragGroup) {
42934                 this.setDraggable(this.dragGroup.split(","));
42935     }
42936     if (this.dropGroup) {
42937                 this.setDroppable(this.dropGroup.split(","));
42938     }
42939     if (this.deletable) {
42940         this.setDeletable();
42941     }
42942     this.isDirtyFlag = false;
42943         this.addEvents({
42944                 "drop" : true
42945         });
42946 };
42947
42948 Roo.extend(Roo.DDView, Roo.View, {
42949 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
42950 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
42951 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
42952 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
42953
42954         isFormField: true,
42955
42956         reset: Roo.emptyFn,
42957         
42958         clearInvalid: Roo.form.Field.prototype.clearInvalid,
42959
42960         validate: function() {
42961                 return true;
42962         },
42963         
42964         destroy: function() {
42965                 this.purgeListeners();
42966                 this.getEl.removeAllListeners();
42967                 this.getEl().remove();
42968                 if (this.dragZone) {
42969                         if (this.dragZone.destroy) {
42970                                 this.dragZone.destroy();
42971                         }
42972                 }
42973                 if (this.dropZone) {
42974                         if (this.dropZone.destroy) {
42975                                 this.dropZone.destroy();
42976                         }
42977                 }
42978         },
42979
42980 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
42981         getName: function() {
42982                 return this.name;
42983         },
42984
42985 /**     Loads the View from a JSON string representing the Records to put into the Store. */
42986         setValue: function(v) {
42987                 if (!this.store) {
42988                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
42989                 }
42990                 var data = {};
42991                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
42992                 this.store.proxy = new Roo.data.MemoryProxy(data);
42993                 this.store.load();
42994         },
42995
42996 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
42997         getValue: function() {
42998                 var result = '(';
42999                 this.store.each(function(rec) {
43000                         result += rec.id + ',';
43001                 });
43002                 return result.substr(0, result.length - 1) + ')';
43003         },
43004         
43005         getIds: function() {
43006                 var i = 0, result = new Array(this.store.getCount());
43007                 this.store.each(function(rec) {
43008                         result[i++] = rec.id;
43009                 });
43010                 return result;
43011         },
43012         
43013         isDirty: function() {
43014                 return this.isDirtyFlag;
43015         },
43016
43017 /**
43018  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43019  *      whole Element becomes the target, and this causes the drop gesture to append.
43020  */
43021     getTargetFromEvent : function(e) {
43022                 var target = e.getTarget();
43023                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43024                 target = target.parentNode;
43025                 }
43026                 if (!target) {
43027                         target = this.el.dom.lastChild || this.el.dom;
43028                 }
43029                 return target;
43030     },
43031
43032 /**
43033  *      Create the drag data which consists of an object which has the property "ddel" as
43034  *      the drag proxy element. 
43035  */
43036     getDragData : function(e) {
43037         var target = this.findItemFromChild(e.getTarget());
43038                 if(target) {
43039                         this.handleSelection(e);
43040                         var selNodes = this.getSelectedNodes();
43041             var dragData = {
43042                 source: this,
43043                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43044                 nodes: selNodes,
43045                 records: []
43046                         };
43047                         var selectedIndices = this.getSelectedIndexes();
43048                         for (var i = 0; i < selectedIndices.length; i++) {
43049                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43050                         }
43051                         if (selNodes.length == 1) {
43052                                 dragData.ddel = target.cloneNode(true); // the div element
43053                         } else {
43054                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43055                                 div.className = 'multi-proxy';
43056                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43057                                         div.appendChild(selNodes[i].cloneNode(true));
43058                                 }
43059                                 dragData.ddel = div;
43060                         }
43061             //console.log(dragData)
43062             //console.log(dragData.ddel.innerHTML)
43063                         return dragData;
43064                 }
43065         //console.log('nodragData')
43066                 return false;
43067     },
43068     
43069 /**     Specify to which ddGroup items in this DDView may be dragged. */
43070     setDraggable: function(ddGroup) {
43071         if (ddGroup instanceof Array) {
43072                 Roo.each(ddGroup, this.setDraggable, this);
43073                 return;
43074         }
43075         if (this.dragZone) {
43076                 this.dragZone.addToGroup(ddGroup);
43077         } else {
43078                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
43079                                 containerScroll: true,
43080                                 ddGroup: ddGroup 
43081
43082                         });
43083 //                      Draggability implies selection. DragZone's mousedown selects the element.
43084                         if (!this.multiSelect) { this.singleSelect = true; }
43085
43086 //                      Wire the DragZone's handlers up to methods in *this*
43087                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
43088                 }
43089     },
43090
43091 /**     Specify from which ddGroup this DDView accepts drops. */
43092     setDroppable: function(ddGroup) {
43093         if (ddGroup instanceof Array) {
43094                 Roo.each(ddGroup, this.setDroppable, this);
43095                 return;
43096         }
43097         if (this.dropZone) {
43098                 this.dropZone.addToGroup(ddGroup);
43099         } else {
43100                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
43101                                 containerScroll: true,
43102                                 ddGroup: ddGroup
43103                         });
43104
43105 //                      Wire the DropZone's handlers up to methods in *this*
43106                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
43107                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
43108                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
43109                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
43110                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
43111                 }
43112     },
43113
43114 /**     Decide whether to drop above or below a View node. */
43115     getDropPoint : function(e, n, dd){
43116         if (n == this.el.dom) { return "above"; }
43117                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
43118                 var c = t + (b - t) / 2;
43119                 var y = Roo.lib.Event.getPageY(e);
43120                 if(y <= c) {
43121                         return "above";
43122                 }else{
43123                         return "below";
43124                 }
43125     },
43126
43127     onNodeEnter : function(n, dd, e, data){
43128                 return false;
43129     },
43130     
43131     onNodeOver : function(n, dd, e, data){
43132                 var pt = this.getDropPoint(e, n, dd);
43133                 // set the insert point style on the target node
43134                 var dragElClass = this.dropNotAllowed;
43135                 if (pt) {
43136                         var targetElClass;
43137                         if (pt == "above"){
43138                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
43139                                 targetElClass = "x-view-drag-insert-above";
43140                         } else {
43141                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
43142                                 targetElClass = "x-view-drag-insert-below";
43143                         }
43144                         if (this.lastInsertClass != targetElClass){
43145                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
43146                                 this.lastInsertClass = targetElClass;
43147                         }
43148                 }
43149                 return dragElClass;
43150         },
43151
43152     onNodeOut : function(n, dd, e, data){
43153                 this.removeDropIndicators(n);
43154     },
43155
43156     onNodeDrop : function(n, dd, e, data){
43157         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
43158                 return false;
43159         }
43160         var pt = this.getDropPoint(e, n, dd);
43161                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
43162                 if (pt == "below") { insertAt++; }
43163                 for (var i = 0; i < data.records.length; i++) {
43164                         var r = data.records[i];
43165                         var dup = this.store.getById(r.id);
43166                         if (dup && (dd != this.dragZone)) {
43167                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
43168                         } else {
43169                                 if (data.copy) {
43170                                         this.store.insert(insertAt++, r.copy());
43171                                 } else {
43172                                         data.source.isDirtyFlag = true;
43173                                         r.store.remove(r);
43174                                         this.store.insert(insertAt++, r);
43175                                 }
43176                                 this.isDirtyFlag = true;
43177                         }
43178                 }
43179                 this.dragZone.cachedTarget = null;
43180                 return true;
43181     },
43182
43183     removeDropIndicators : function(n){
43184                 if(n){
43185                         Roo.fly(n).removeClass([
43186                                 "x-view-drag-insert-above",
43187                                 "x-view-drag-insert-below"]);
43188                         this.lastInsertClass = "_noclass";
43189                 }
43190     },
43191
43192 /**
43193  *      Utility method. Add a delete option to the DDView's context menu.
43194  *      @param {String} imageUrl The URL of the "delete" icon image.
43195  */
43196         setDeletable: function(imageUrl) {
43197                 if (!this.singleSelect && !this.multiSelect) {
43198                         this.singleSelect = true;
43199                 }
43200                 var c = this.getContextMenu();
43201                 this.contextMenu.on("itemclick", function(item) {
43202                         switch (item.id) {
43203                                 case "delete":
43204                                         this.remove(this.getSelectedIndexes());
43205                                         break;
43206                         }
43207                 }, this);
43208                 this.contextMenu.add({
43209                         icon: imageUrl,
43210                         id: "delete",
43211                         text: 'Delete'
43212                 });
43213         },
43214         
43215 /**     Return the context menu for this DDView. */
43216         getContextMenu: function() {
43217                 if (!this.contextMenu) {
43218 //                      Create the View's context menu
43219                         this.contextMenu = new Roo.menu.Menu({
43220                                 id: this.id + "-contextmenu"
43221                         });
43222                         this.el.on("contextmenu", this.showContextMenu, this);
43223                 }
43224                 return this.contextMenu;
43225         },
43226         
43227         disableContextMenu: function() {
43228                 if (this.contextMenu) {
43229                         this.el.un("contextmenu", this.showContextMenu, this);
43230                 }
43231         },
43232
43233         showContextMenu: function(e, item) {
43234         item = this.findItemFromChild(e.getTarget());
43235                 if (item) {
43236                         e.stopEvent();
43237                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
43238                         this.contextMenu.showAt(e.getXY());
43239             }
43240     },
43241
43242 /**
43243  *      Remove {@link Roo.data.Record}s at the specified indices.
43244  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
43245  */
43246     remove: function(selectedIndices) {
43247                 selectedIndices = [].concat(selectedIndices);
43248                 for (var i = 0; i < selectedIndices.length; i++) {
43249                         var rec = this.store.getAt(selectedIndices[i]);
43250                         this.store.remove(rec);
43251                 }
43252     },
43253
43254 /**
43255  *      Double click fires the event, but also, if this is draggable, and there is only one other
43256  *      related DropZone, it transfers the selected node.
43257  */
43258     onDblClick : function(e){
43259         var item = this.findItemFromChild(e.getTarget());
43260         if(item){
43261             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
43262                 return false;
43263             }
43264             if (this.dragGroup) {
43265                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
43266                     while (targets.indexOf(this.dropZone) > -1) {
43267                             targets.remove(this.dropZone);
43268                                 }
43269                     if (targets.length == 1) {
43270                                         this.dragZone.cachedTarget = null;
43271                         var el = Roo.get(targets[0].getEl());
43272                         var box = el.getBox(true);
43273                         targets[0].onNodeDrop(el.dom, {
43274                                 target: el.dom,
43275                                 xy: [box.x, box.y + box.height - 1]
43276                         }, null, this.getDragData(e));
43277                     }
43278                 }
43279         }
43280     },
43281     
43282     handleSelection: function(e) {
43283                 this.dragZone.cachedTarget = null;
43284         var item = this.findItemFromChild(e.getTarget());
43285         if (!item) {
43286                 this.clearSelections(true);
43287                 return;
43288         }
43289                 if (item && (this.multiSelect || this.singleSelect)){
43290                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
43291                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
43292                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
43293                                 this.unselect(item);
43294                         } else {
43295                                 this.select(item, this.multiSelect && e.ctrlKey);
43296                                 this.lastSelection = item;
43297                         }
43298                 }
43299     },
43300
43301     onItemClick : function(item, index, e){
43302                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
43303                         return false;
43304                 }
43305                 return true;
43306     },
43307
43308     unselect : function(nodeInfo, suppressEvent){
43309                 var node = this.getNode(nodeInfo);
43310                 if(node && this.isSelected(node)){
43311                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
43312                                 Roo.fly(node).removeClass(this.selectedClass);
43313                                 this.selections.remove(node);
43314                                 if(!suppressEvent){
43315                                         this.fireEvent("selectionchange", this, this.selections);
43316                                 }
43317                         }
43318                 }
43319     }
43320 });
43321 /*
43322  * Based on:
43323  * Ext JS Library 1.1.1
43324  * Copyright(c) 2006-2007, Ext JS, LLC.
43325  *
43326  * Originally Released Under LGPL - original licence link has changed is not relivant.
43327  *
43328  * Fork - LGPL
43329  * <script type="text/javascript">
43330  */
43331  
43332 /**
43333  * @class Roo.LayoutManager
43334  * @extends Roo.util.Observable
43335  * Base class for layout managers.
43336  */
43337 Roo.LayoutManager = function(container, config){
43338     Roo.LayoutManager.superclass.constructor.call(this);
43339     this.el = Roo.get(container);
43340     // ie scrollbar fix
43341     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
43342         document.body.scroll = "no";
43343     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
43344         this.el.position('relative');
43345     }
43346     this.id = this.el.id;
43347     this.el.addClass("x-layout-container");
43348     /** false to disable window resize monitoring @type Boolean */
43349     this.monitorWindowResize = true;
43350     this.regions = {};
43351     this.addEvents({
43352         /**
43353          * @event layout
43354          * Fires when a layout is performed. 
43355          * @param {Roo.LayoutManager} this
43356          */
43357         "layout" : true,
43358         /**
43359          * @event regionresized
43360          * Fires when the user resizes a region. 
43361          * @param {Roo.LayoutRegion} region The resized region
43362          * @param {Number} newSize The new size (width for east/west, height for north/south)
43363          */
43364         "regionresized" : true,
43365         /**
43366          * @event regioncollapsed
43367          * Fires when a region is collapsed. 
43368          * @param {Roo.LayoutRegion} region The collapsed region
43369          */
43370         "regioncollapsed" : true,
43371         /**
43372          * @event regionexpanded
43373          * Fires when a region is expanded.  
43374          * @param {Roo.LayoutRegion} region The expanded region
43375          */
43376         "regionexpanded" : true
43377     });
43378     this.updating = false;
43379     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
43380 };
43381
43382 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
43383     /**
43384      * Returns true if this layout is currently being updated
43385      * @return {Boolean}
43386      */
43387     isUpdating : function(){
43388         return this.updating; 
43389     },
43390     
43391     /**
43392      * Suspend the LayoutManager from doing auto-layouts while
43393      * making multiple add or remove calls
43394      */
43395     beginUpdate : function(){
43396         this.updating = true;    
43397     },
43398     
43399     /**
43400      * Restore auto-layouts and optionally disable the manager from performing a layout
43401      * @param {Boolean} noLayout true to disable a layout update 
43402      */
43403     endUpdate : function(noLayout){
43404         this.updating = false;
43405         if(!noLayout){
43406             this.layout();
43407         }    
43408     },
43409     
43410     layout: function(){
43411         
43412     },
43413     
43414     onRegionResized : function(region, newSize){
43415         this.fireEvent("regionresized", region, newSize);
43416         this.layout();
43417     },
43418     
43419     onRegionCollapsed : function(region){
43420         this.fireEvent("regioncollapsed", region);
43421     },
43422     
43423     onRegionExpanded : function(region){
43424         this.fireEvent("regionexpanded", region);
43425     },
43426         
43427     /**
43428      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
43429      * performs box-model adjustments.
43430      * @return {Object} The size as an object {width: (the width), height: (the height)}
43431      */
43432     getViewSize : function(){
43433         var size;
43434         if(this.el.dom != document.body){
43435             size = this.el.getSize();
43436         }else{
43437             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43438         }
43439         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43440         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43441         return size;
43442     },
43443     
43444     /**
43445      * Returns the Element this layout is bound to.
43446      * @return {Roo.Element}
43447      */
43448     getEl : function(){
43449         return this.el;
43450     },
43451     
43452     /**
43453      * Returns the specified region.
43454      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43455      * @return {Roo.LayoutRegion}
43456      */
43457     getRegion : function(target){
43458         return this.regions[target.toLowerCase()];
43459     },
43460     
43461     onWindowResize : function(){
43462         if(this.monitorWindowResize){
43463             this.layout();
43464         }
43465     }
43466 });/*
43467  * Based on:
43468  * Ext JS Library 1.1.1
43469  * Copyright(c) 2006-2007, Ext JS, LLC.
43470  *
43471  * Originally Released Under LGPL - original licence link has changed is not relivant.
43472  *
43473  * Fork - LGPL
43474  * <script type="text/javascript">
43475  */
43476 /**
43477  * @class Roo.BorderLayout
43478  * @extends Roo.LayoutManager
43479  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43480  * please see: <br><br>
43481  * <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>
43482  * <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>
43483  * Example:
43484  <pre><code>
43485  var layout = new Roo.BorderLayout(document.body, {
43486     north: {
43487         initialSize: 25,
43488         titlebar: false
43489     },
43490     west: {
43491         split:true,
43492         initialSize: 200,
43493         minSize: 175,
43494         maxSize: 400,
43495         titlebar: true,
43496         collapsible: true
43497     },
43498     east: {
43499         split:true,
43500         initialSize: 202,
43501         minSize: 175,
43502         maxSize: 400,
43503         titlebar: true,
43504         collapsible: true
43505     },
43506     south: {
43507         split:true,
43508         initialSize: 100,
43509         minSize: 100,
43510         maxSize: 200,
43511         titlebar: true,
43512         collapsible: true
43513     },
43514     center: {
43515         titlebar: true,
43516         autoScroll:true,
43517         resizeTabs: true,
43518         minTabWidth: 50,
43519         preferredTabWidth: 150
43520     }
43521 });
43522
43523 // shorthand
43524 var CP = Roo.ContentPanel;
43525
43526 layout.beginUpdate();
43527 layout.add("north", new CP("north", "North"));
43528 layout.add("south", new CP("south", {title: "South", closable: true}));
43529 layout.add("west", new CP("west", {title: "West"}));
43530 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
43531 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
43532 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
43533 layout.getRegion("center").showPanel("center1");
43534 layout.endUpdate();
43535 </code></pre>
43536
43537 <b>The container the layout is rendered into can be either the body element or any other element.
43538 If it is not the body element, the container needs to either be an absolute positioned element,
43539 or you will need to add "position:relative" to the css of the container.  You will also need to specify
43540 the container size if it is not the body element.</b>
43541
43542 * @constructor
43543 * Create a new BorderLayout
43544 * @param {String/HTMLElement/Element} container The container this layout is bound to
43545 * @param {Object} config Configuration options
43546  */
43547 Roo.BorderLayout = function(container, config){
43548     config = config || {};
43549     Roo.BorderLayout.superclass.constructor.call(this, container, config);
43550     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
43551     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
43552         var target = this.factory.validRegions[i];
43553         if(config[target]){
43554             this.addRegion(target, config[target]);
43555         }
43556     }
43557 };
43558
43559 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
43560     /**
43561      * Creates and adds a new region if it doesn't already exist.
43562      * @param {String} target The target region key (north, south, east, west or center).
43563      * @param {Object} config The regions config object
43564      * @return {BorderLayoutRegion} The new region
43565      */
43566     addRegion : function(target, config){
43567         if(!this.regions[target]){
43568             var r = this.factory.create(target, this, config);
43569             this.bindRegion(target, r);
43570         }
43571         return this.regions[target];
43572     },
43573
43574     // private (kinda)
43575     bindRegion : function(name, r){
43576         this.regions[name] = r;
43577         r.on("visibilitychange", this.layout, this);
43578         r.on("paneladded", this.layout, this);
43579         r.on("panelremoved", this.layout, this);
43580         r.on("invalidated", this.layout, this);
43581         r.on("resized", this.onRegionResized, this);
43582         r.on("collapsed", this.onRegionCollapsed, this);
43583         r.on("expanded", this.onRegionExpanded, this);
43584     },
43585
43586     /**
43587      * Performs a layout update.
43588      */
43589     layout : function(){
43590         if(this.updating) return;
43591         var size = this.getViewSize();
43592         var w = size.width;
43593         var h = size.height;
43594         var centerW = w;
43595         var centerH = h;
43596         var centerY = 0;
43597         var centerX = 0;
43598         //var x = 0, y = 0;
43599
43600         var rs = this.regions;
43601         var north = rs["north"];
43602         var south = rs["south"]; 
43603         var west = rs["west"];
43604         var east = rs["east"];
43605         var center = rs["center"];
43606         //if(this.hideOnLayout){ // not supported anymore
43607             //c.el.setStyle("display", "none");
43608         //}
43609         if(north && north.isVisible()){
43610             var b = north.getBox();
43611             var m = north.getMargins();
43612             b.width = w - (m.left+m.right);
43613             b.x = m.left;
43614             b.y = m.top;
43615             centerY = b.height + b.y + m.bottom;
43616             centerH -= centerY;
43617             north.updateBox(this.safeBox(b));
43618         }
43619         if(south && south.isVisible()){
43620             var b = south.getBox();
43621             var m = south.getMargins();
43622             b.width = w - (m.left+m.right);
43623             b.x = m.left;
43624             var totalHeight = (b.height + m.top + m.bottom);
43625             b.y = h - totalHeight + m.top;
43626             centerH -= totalHeight;
43627             south.updateBox(this.safeBox(b));
43628         }
43629         if(west && west.isVisible()){
43630             var b = west.getBox();
43631             var m = west.getMargins();
43632             b.height = centerH - (m.top+m.bottom);
43633             b.x = m.left;
43634             b.y = centerY + m.top;
43635             var totalWidth = (b.width + m.left + m.right);
43636             centerX += totalWidth;
43637             centerW -= totalWidth;
43638             west.updateBox(this.safeBox(b));
43639         }
43640         if(east && east.isVisible()){
43641             var b = east.getBox();
43642             var m = east.getMargins();
43643             b.height = centerH - (m.top+m.bottom);
43644             var totalWidth = (b.width + m.left + m.right);
43645             b.x = w - totalWidth + m.left;
43646             b.y = centerY + m.top;
43647             centerW -= totalWidth;
43648             east.updateBox(this.safeBox(b));
43649         }
43650         if(center){
43651             var m = center.getMargins();
43652             var centerBox = {
43653                 x: centerX + m.left,
43654                 y: centerY + m.top,
43655                 width: centerW - (m.left+m.right),
43656                 height: centerH - (m.top+m.bottom)
43657             };
43658             //if(this.hideOnLayout){
43659                 //center.el.setStyle("display", "block");
43660             //}
43661             center.updateBox(this.safeBox(centerBox));
43662         }
43663         this.el.repaint();
43664         this.fireEvent("layout", this);
43665     },
43666
43667     // private
43668     safeBox : function(box){
43669         box.width = Math.max(0, box.width);
43670         box.height = Math.max(0, box.height);
43671         return box;
43672     },
43673
43674     /**
43675      * Adds a ContentPanel (or subclass) to this layout.
43676      * @param {String} target The target region key (north, south, east, west or center).
43677      * @param {Roo.ContentPanel} panel The panel to add
43678      * @return {Roo.ContentPanel} The added panel
43679      */
43680     add : function(target, panel){
43681          
43682         target = target.toLowerCase();
43683         return this.regions[target].add(panel);
43684     },
43685
43686     /**
43687      * Remove a ContentPanel (or subclass) to this layout.
43688      * @param {String} target The target region key (north, south, east, west or center).
43689      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43690      * @return {Roo.ContentPanel} The removed panel
43691      */
43692     remove : function(target, panel){
43693         target = target.toLowerCase();
43694         return this.regions[target].remove(panel);
43695     },
43696
43697     /**
43698      * Searches all regions for a panel with the specified id
43699      * @param {String} panelId
43700      * @return {Roo.ContentPanel} The panel or null if it wasn't found
43701      */
43702     findPanel : function(panelId){
43703         var rs = this.regions;
43704         for(var target in rs){
43705             if(typeof rs[target] != "function"){
43706                 var p = rs[target].getPanel(panelId);
43707                 if(p){
43708                     return p;
43709                 }
43710             }
43711         }
43712         return null;
43713     },
43714
43715     /**
43716      * Searches all regions for a panel with the specified id and activates (shows) it.
43717      * @param {String/ContentPanel} panelId The panels id or the panel itself
43718      * @return {Roo.ContentPanel} The shown panel or null
43719      */
43720     showPanel : function(panelId) {
43721       var rs = this.regions;
43722       for(var target in rs){
43723          var r = rs[target];
43724          if(typeof r != "function"){
43725             if(r.hasPanel(panelId)){
43726                return r.showPanel(panelId);
43727             }
43728          }
43729       }
43730       return null;
43731    },
43732
43733    /**
43734      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43735      * @param {Roo.state.Provider} provider (optional) An alternate state provider
43736      */
43737     restoreState : function(provider){
43738         if(!provider){
43739             provider = Roo.state.Manager;
43740         }
43741         var sm = new Roo.LayoutStateManager();
43742         sm.init(this, provider);
43743     },
43744
43745     /**
43746      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
43747      * object should contain properties for each region to add ContentPanels to, and each property's value should be
43748      * a valid ContentPanel config object.  Example:
43749      * <pre><code>
43750 // Create the main layout
43751 var layout = new Roo.BorderLayout('main-ct', {
43752     west: {
43753         split:true,
43754         minSize: 175,
43755         titlebar: true
43756     },
43757     center: {
43758         title:'Components'
43759     }
43760 }, 'main-ct');
43761
43762 // Create and add multiple ContentPanels at once via configs
43763 layout.batchAdd({
43764    west: {
43765        id: 'source-files',
43766        autoCreate:true,
43767        title:'Ext Source Files',
43768        autoScroll:true,
43769        fitToFrame:true
43770    },
43771    center : {
43772        el: cview,
43773        autoScroll:true,
43774        fitToFrame:true,
43775        toolbar: tb,
43776        resizeEl:'cbody'
43777    }
43778 });
43779 </code></pre>
43780      * @param {Object} regions An object containing ContentPanel configs by region name
43781      */
43782     batchAdd : function(regions){
43783         this.beginUpdate();
43784         for(var rname in regions){
43785             var lr = this.regions[rname];
43786             if(lr){
43787                 this.addTypedPanels(lr, regions[rname]);
43788             }
43789         }
43790         this.endUpdate();
43791     },
43792
43793     // private
43794     addTypedPanels : function(lr, ps){
43795         if(typeof ps == 'string'){
43796             lr.add(new Roo.ContentPanel(ps));
43797         }
43798         else if(ps instanceof Array){
43799             for(var i =0, len = ps.length; i < len; i++){
43800                 this.addTypedPanels(lr, ps[i]);
43801             }
43802         }
43803         else if(!ps.events){ // raw config?
43804             var el = ps.el;
43805             delete ps.el; // prevent conflict
43806             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
43807         }
43808         else {  // panel object assumed!
43809             lr.add(ps);
43810         }
43811     },
43812     /**
43813      * Adds a xtype elements to the layout.
43814      * <pre><code>
43815
43816 layout.addxtype({
43817        xtype : 'ContentPanel',
43818        region: 'west',
43819        items: [ .... ]
43820    }
43821 );
43822
43823 layout.addxtype({
43824         xtype : 'NestedLayoutPanel',
43825         region: 'west',
43826         layout: {
43827            center: { },
43828            west: { }   
43829         },
43830         items : [ ... list of content panels or nested layout panels.. ]
43831    }
43832 );
43833 </code></pre>
43834      * @param {Object} cfg Xtype definition of item to add.
43835      */
43836     addxtype : function(cfg)
43837     {
43838         // basically accepts a pannel...
43839         // can accept a layout region..!?!?
43840        // console.log('BorderLayout add ' + cfg.xtype)
43841         
43842         if (!cfg.xtype.match(/Panel$/)) {
43843             return false;
43844         }
43845         var ret = false;
43846         var region = cfg.region;
43847         delete cfg.region;
43848         
43849           
43850         var xitems = [];
43851         if (cfg.items) {
43852             xitems = cfg.items;
43853             delete cfg.items;
43854         }
43855         
43856         
43857         switch(cfg.xtype) 
43858         {
43859             case 'ContentPanel':  // ContentPanel (el, cfg)
43860             case 'ScrollPanel':  // ContentPanel (el, cfg)
43861                 if(cfg.autoCreate) {
43862                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43863                 } else {
43864                     var el = this.el.createChild();
43865                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43866                 }
43867                 
43868                 this.add(region, ret);
43869                 break;
43870             
43871             
43872             case 'TreePanel': // our new panel!
43873                 cfg.el = this.el.createChild();
43874                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43875                 this.add(region, ret);
43876                 break;
43877             
43878             case 'NestedLayoutPanel': 
43879                 // create a new Layout (which is  a Border Layout...
43880                 var el = this.el.createChild();
43881                 var clayout = cfg.layout;
43882                 delete cfg.layout;
43883                 clayout.items   = clayout.items  || [];
43884                 // replace this exitems with the clayout ones..
43885                 xitems = clayout.items;
43886                  
43887                 
43888                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43889                     cfg.background = false;
43890                 }
43891                 var layout = new Roo.BorderLayout(el, clayout);
43892                 
43893                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
43894                 //console.log('adding nested layout panel '  + cfg.toSource());
43895                 this.add(region, ret);
43896                 
43897                 break;
43898                 
43899             case 'GridPanel': 
43900             
43901                 // needs grid and region
43902                 
43903                 //var el = this.getRegion(region).el.createChild();
43904                 var el = this.el.createChild();
43905                 // create the grid first...
43906                 
43907                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
43908                 delete cfg.grid;
43909                 if (region == 'center' && this.active ) {
43910                     cfg.background = false;
43911                 }
43912                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
43913                 
43914                 this.add(region, ret);
43915                 if (cfg.background) {
43916                     ret.on('activate', function(gp) {
43917                         if (!gp.grid.rendered) {
43918                             gp.grid.render();
43919                         }
43920                     });
43921                 } else {
43922                     grid.render();
43923                 }
43924                 break;
43925            
43926                
43927                 
43928                 
43929             default: 
43930                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
43931                 return;
43932              // GridPanel (grid, cfg)
43933             
43934         }
43935         this.beginUpdate();
43936         // add children..
43937         Roo.each(xitems, function(i)  {
43938             ret.addxtype(i);
43939         });
43940         this.endUpdate();
43941         return ret;
43942         
43943     }
43944 });
43945
43946 /**
43947  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
43948  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
43949  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
43950  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
43951  * <pre><code>
43952 // shorthand
43953 var CP = Roo.ContentPanel;
43954
43955 var layout = Roo.BorderLayout.create({
43956     north: {
43957         initialSize: 25,
43958         titlebar: false,
43959         panels: [new CP("north", "North")]
43960     },
43961     west: {
43962         split:true,
43963         initialSize: 200,
43964         minSize: 175,
43965         maxSize: 400,
43966         titlebar: true,
43967         collapsible: true,
43968         panels: [new CP("west", {title: "West"})]
43969     },
43970     east: {
43971         split:true,
43972         initialSize: 202,
43973         minSize: 175,
43974         maxSize: 400,
43975         titlebar: true,
43976         collapsible: true,
43977         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
43978     },
43979     south: {
43980         split:true,
43981         initialSize: 100,
43982         minSize: 100,
43983         maxSize: 200,
43984         titlebar: true,
43985         collapsible: true,
43986         panels: [new CP("south", {title: "South", closable: true})]
43987     },
43988     center: {
43989         titlebar: true,
43990         autoScroll:true,
43991         resizeTabs: true,
43992         minTabWidth: 50,
43993         preferredTabWidth: 150,
43994         panels: [
43995             new CP("center1", {title: "Close Me", closable: true}),
43996             new CP("center2", {title: "Center Panel", closable: false})
43997         ]
43998     }
43999 }, document.body);
44000
44001 layout.getRegion("center").showPanel("center1");
44002 </code></pre>
44003  * @param config
44004  * @param targetEl
44005  */
44006 Roo.BorderLayout.create = function(config, targetEl){
44007     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44008     layout.beginUpdate();
44009     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44010     for(var j = 0, jlen = regions.length; j < jlen; j++){
44011         var lr = regions[j];
44012         if(layout.regions[lr] && config[lr].panels){
44013             var r = layout.regions[lr];
44014             var ps = config[lr].panels;
44015             layout.addTypedPanels(r, ps);
44016         }
44017     }
44018     layout.endUpdate();
44019     return layout;
44020 };
44021
44022 // private
44023 Roo.BorderLayout.RegionFactory = {
44024     // private
44025     validRegions : ["north","south","east","west","center"],
44026
44027     // private
44028     create : function(target, mgr, config){
44029         target = target.toLowerCase();
44030         if(config.lightweight || config.basic){
44031             return new Roo.BasicLayoutRegion(mgr, config, target);
44032         }
44033         switch(target){
44034             case "north":
44035                 return new Roo.NorthLayoutRegion(mgr, config);
44036             case "south":
44037                 return new Roo.SouthLayoutRegion(mgr, config);
44038             case "east":
44039                 return new Roo.EastLayoutRegion(mgr, config);
44040             case "west":
44041                 return new Roo.WestLayoutRegion(mgr, config);
44042             case "center":
44043                 return new Roo.CenterLayoutRegion(mgr, config);
44044         }
44045         throw 'Layout region "'+target+'" not supported.';
44046     }
44047 };/*
44048  * Based on:
44049  * Ext JS Library 1.1.1
44050  * Copyright(c) 2006-2007, Ext JS, LLC.
44051  *
44052  * Originally Released Under LGPL - original licence link has changed is not relivant.
44053  *
44054  * Fork - LGPL
44055  * <script type="text/javascript">
44056  */
44057  
44058 /**
44059  * @class Roo.BasicLayoutRegion
44060  * @extends Roo.util.Observable
44061  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
44062  * and does not have a titlebar, tabs or any other features. All it does is size and position 
44063  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
44064  */
44065 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
44066     this.mgr = mgr;
44067     this.position  = pos;
44068     this.events = {
44069         /**
44070          * @scope Roo.BasicLayoutRegion
44071          */
44072         
44073         /**
44074          * @event beforeremove
44075          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
44076          * @param {Roo.LayoutRegion} this
44077          * @param {Roo.ContentPanel} panel The panel
44078          * @param {Object} e The cancel event object
44079          */
44080         "beforeremove" : true,
44081         /**
44082          * @event invalidated
44083          * Fires when the layout for this region is changed.
44084          * @param {Roo.LayoutRegion} this
44085          */
44086         "invalidated" : true,
44087         /**
44088          * @event visibilitychange
44089          * Fires when this region is shown or hidden 
44090          * @param {Roo.LayoutRegion} this
44091          * @param {Boolean} visibility true or false
44092          */
44093         "visibilitychange" : true,
44094         /**
44095          * @event paneladded
44096          * Fires when a panel is added. 
44097          * @param {Roo.LayoutRegion} this
44098          * @param {Roo.ContentPanel} panel The panel
44099          */
44100         "paneladded" : true,
44101         /**
44102          * @event panelremoved
44103          * Fires when a panel is removed. 
44104          * @param {Roo.LayoutRegion} this
44105          * @param {Roo.ContentPanel} panel The panel
44106          */
44107         "panelremoved" : true,
44108         /**
44109          * @event collapsed
44110          * Fires when this region is collapsed.
44111          * @param {Roo.LayoutRegion} this
44112          */
44113         "collapsed" : true,
44114         /**
44115          * @event expanded
44116          * Fires when this region is expanded.
44117          * @param {Roo.LayoutRegion} this
44118          */
44119         "expanded" : true,
44120         /**
44121          * @event slideshow
44122          * Fires when this region is slid into view.
44123          * @param {Roo.LayoutRegion} this
44124          */
44125         "slideshow" : true,
44126         /**
44127          * @event slidehide
44128          * Fires when this region slides out of view. 
44129          * @param {Roo.LayoutRegion} this
44130          */
44131         "slidehide" : true,
44132         /**
44133          * @event panelactivated
44134          * Fires when a panel is activated. 
44135          * @param {Roo.LayoutRegion} this
44136          * @param {Roo.ContentPanel} panel The activated panel
44137          */
44138         "panelactivated" : true,
44139         /**
44140          * @event resized
44141          * Fires when the user resizes this region. 
44142          * @param {Roo.LayoutRegion} this
44143          * @param {Number} newSize The new size (width for east/west, height for north/south)
44144          */
44145         "resized" : true
44146     };
44147     /** A collection of panels in this region. @type Roo.util.MixedCollection */
44148     this.panels = new Roo.util.MixedCollection();
44149     this.panels.getKey = this.getPanelId.createDelegate(this);
44150     this.box = null;
44151     this.activePanel = null;
44152     // ensure listeners are added...
44153     
44154     if (config.listeners || config.events) {
44155         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
44156             listeners : config.listeners || {},
44157             events : config.events || {}
44158         });
44159     }
44160     
44161     if(skipConfig !== true){
44162         this.applyConfig(config);
44163     }
44164 };
44165
44166 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
44167     getPanelId : function(p){
44168         return p.getId();
44169     },
44170     
44171     applyConfig : function(config){
44172         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44173         this.config = config;
44174         
44175     },
44176     
44177     /**
44178      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
44179      * the width, for horizontal (north, south) the height.
44180      * @param {Number} newSize The new width or height
44181      */
44182     resizeTo : function(newSize){
44183         var el = this.el ? this.el :
44184                  (this.activePanel ? this.activePanel.getEl() : null);
44185         if(el){
44186             switch(this.position){
44187                 case "east":
44188                 case "west":
44189                     el.setWidth(newSize);
44190                     this.fireEvent("resized", this, newSize);
44191                 break;
44192                 case "north":
44193                 case "south":
44194                     el.setHeight(newSize);
44195                     this.fireEvent("resized", this, newSize);
44196                 break;                
44197             }
44198         }
44199     },
44200     
44201     getBox : function(){
44202         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
44203     },
44204     
44205     getMargins : function(){
44206         return this.margins;
44207     },
44208     
44209     updateBox : function(box){
44210         this.box = box;
44211         var el = this.activePanel.getEl();
44212         el.dom.style.left = box.x + "px";
44213         el.dom.style.top = box.y + "px";
44214         this.activePanel.setSize(box.width, box.height);
44215     },
44216     
44217     /**
44218      * Returns the container element for this region.
44219      * @return {Roo.Element}
44220      */
44221     getEl : function(){
44222         return this.activePanel;
44223     },
44224     
44225     /**
44226      * Returns true if this region is currently visible.
44227      * @return {Boolean}
44228      */
44229     isVisible : function(){
44230         return this.activePanel ? true : false;
44231     },
44232     
44233     setActivePanel : function(panel){
44234         panel = this.getPanel(panel);
44235         if(this.activePanel && this.activePanel != panel){
44236             this.activePanel.setActiveState(false);
44237             this.activePanel.getEl().setLeftTop(-10000,-10000);
44238         }
44239         this.activePanel = panel;
44240         panel.setActiveState(true);
44241         if(this.box){
44242             panel.setSize(this.box.width, this.box.height);
44243         }
44244         this.fireEvent("panelactivated", this, panel);
44245         this.fireEvent("invalidated");
44246     },
44247     
44248     /**
44249      * Show the specified panel.
44250      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
44251      * @return {Roo.ContentPanel} The shown panel or null
44252      */
44253     showPanel : function(panel){
44254         if(panel = this.getPanel(panel)){
44255             this.setActivePanel(panel);
44256         }
44257         return panel;
44258     },
44259     
44260     /**
44261      * Get the active panel for this region.
44262      * @return {Roo.ContentPanel} The active panel or null
44263      */
44264     getActivePanel : function(){
44265         return this.activePanel;
44266     },
44267     
44268     /**
44269      * Add the passed ContentPanel(s)
44270      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44271      * @return {Roo.ContentPanel} The panel added (if only one was added)
44272      */
44273     add : function(panel){
44274         if(arguments.length > 1){
44275             for(var i = 0, len = arguments.length; i < len; i++) {
44276                 this.add(arguments[i]);
44277             }
44278             return null;
44279         }
44280         if(this.hasPanel(panel)){
44281             this.showPanel(panel);
44282             return panel;
44283         }
44284         var el = panel.getEl();
44285         if(el.dom.parentNode != this.mgr.el.dom){
44286             this.mgr.el.dom.appendChild(el.dom);
44287         }
44288         if(panel.setRegion){
44289             panel.setRegion(this);
44290         }
44291         this.panels.add(panel);
44292         el.setStyle("position", "absolute");
44293         if(!panel.background){
44294             this.setActivePanel(panel);
44295             if(this.config.initialSize && this.panels.getCount()==1){
44296                 this.resizeTo(this.config.initialSize);
44297             }
44298         }
44299         this.fireEvent("paneladded", this, panel);
44300         return panel;
44301     },
44302     
44303     /**
44304      * Returns true if the panel is in this region.
44305      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44306      * @return {Boolean}
44307      */
44308     hasPanel : function(panel){
44309         if(typeof panel == "object"){ // must be panel obj
44310             panel = panel.getId();
44311         }
44312         return this.getPanel(panel) ? true : false;
44313     },
44314     
44315     /**
44316      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44317      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44318      * @param {Boolean} preservePanel Overrides the config preservePanel option
44319      * @return {Roo.ContentPanel} The panel that was removed
44320      */
44321     remove : function(panel, preservePanel){
44322         panel = this.getPanel(panel);
44323         if(!panel){
44324             return null;
44325         }
44326         var e = {};
44327         this.fireEvent("beforeremove", this, panel, e);
44328         if(e.cancel === true){
44329             return null;
44330         }
44331         var panelId = panel.getId();
44332         this.panels.removeKey(panelId);
44333         return panel;
44334     },
44335     
44336     /**
44337      * Returns the panel specified or null if it's not in this region.
44338      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44339      * @return {Roo.ContentPanel}
44340      */
44341     getPanel : function(id){
44342         if(typeof id == "object"){ // must be panel obj
44343             return id;
44344         }
44345         return this.panels.get(id);
44346     },
44347     
44348     /**
44349      * Returns this regions position (north/south/east/west/center).
44350      * @return {String} 
44351      */
44352     getPosition: function(){
44353         return this.position;    
44354     }
44355 });/*
44356  * Based on:
44357  * Ext JS Library 1.1.1
44358  * Copyright(c) 2006-2007, Ext JS, LLC.
44359  *
44360  * Originally Released Under LGPL - original licence link has changed is not relivant.
44361  *
44362  * Fork - LGPL
44363  * <script type="text/javascript">
44364  */
44365  
44366 /**
44367  * @class Roo.LayoutRegion
44368  * @extends Roo.BasicLayoutRegion
44369  * This class represents a region in a layout manager.
44370  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
44371  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
44372  * @cfg {Boolean} floatable False to disable floating (defaults to true)
44373  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
44374  * @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})
44375  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
44376  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
44377  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
44378  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
44379  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
44380  * @cfg {String} title The title for the region (overrides panel titles)
44381  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
44382  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
44383  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
44384  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
44385  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
44386  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
44387  * the space available, similar to FireFox 1.5 tabs (defaults to false)
44388  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
44389  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
44390  * @cfg {Boolean} showPin True to show a pin button
44391 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
44392 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
44393 * @cfg {Boolean} disableTabTips True to disable tab tooltips
44394 * @cfg {Number} width  For East/West panels
44395 * @cfg {Number} height For North/South panels
44396 * @cfg {Boolean} split To show the splitter
44397  */
44398 Roo.LayoutRegion = function(mgr, config, pos){
44399     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
44400     var dh = Roo.DomHelper;
44401     /** This region's container element 
44402     * @type Roo.Element */
44403     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
44404     /** This region's title element 
44405     * @type Roo.Element */
44406
44407     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
44408         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
44409         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
44410     ]}, true);
44411     this.titleEl.enableDisplayMode();
44412     /** This region's title text element 
44413     * @type HTMLElement */
44414     this.titleTextEl = this.titleEl.dom.firstChild;
44415     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
44416     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
44417     this.closeBtn.enableDisplayMode();
44418     this.closeBtn.on("click", this.closeClicked, this);
44419     this.closeBtn.hide();
44420
44421     this.createBody(config);
44422     this.visible = true;
44423     this.collapsed = false;
44424
44425     if(config.hideWhenEmpty){
44426         this.hide();
44427         this.on("paneladded", this.validateVisibility, this);
44428         this.on("panelremoved", this.validateVisibility, this);
44429     }
44430     this.applyConfig(config);
44431 };
44432
44433 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
44434
44435     createBody : function(){
44436         /** This region's body element 
44437         * @type Roo.Element */
44438         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
44439     },
44440
44441     applyConfig : function(c){
44442         if(c.collapsible && this.position != "center" && !this.collapsedEl){
44443             var dh = Roo.DomHelper;
44444             if(c.titlebar !== false){
44445                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
44446                 this.collapseBtn.on("click", this.collapse, this);
44447                 this.collapseBtn.enableDisplayMode();
44448
44449                 if(c.showPin === true || this.showPin){
44450                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
44451                     this.stickBtn.enableDisplayMode();
44452                     this.stickBtn.on("click", this.expand, this);
44453                     this.stickBtn.hide();
44454                 }
44455             }
44456             /** This region's collapsed element
44457             * @type Roo.Element */
44458             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44459                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44460             ]}, true);
44461             if(c.floatable !== false){
44462                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44463                this.collapsedEl.on("click", this.collapseClick, this);
44464             }
44465
44466             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44467                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44468                    id: "message", unselectable: "on", style:{"float":"left"}});
44469                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44470              }
44471             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44472             this.expandBtn.on("click", this.expand, this);
44473         }
44474         if(this.collapseBtn){
44475             this.collapseBtn.setVisible(c.collapsible == true);
44476         }
44477         this.cmargins = c.cmargins || this.cmargins ||
44478                          (this.position == "west" || this.position == "east" ?
44479                              {top: 0, left: 2, right:2, bottom: 0} :
44480                              {top: 2, left: 0, right:0, bottom: 2});
44481         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44482         this.bottomTabs = c.tabPosition != "top";
44483         this.autoScroll = c.autoScroll || false;
44484         if(this.autoScroll){
44485             this.bodyEl.setStyle("overflow", "auto");
44486         }else{
44487             this.bodyEl.setStyle("overflow", "hidden");
44488         }
44489         //if(c.titlebar !== false){
44490             if((!c.titlebar && !c.title) || c.titlebar === false){
44491                 this.titleEl.hide();
44492             }else{
44493                 this.titleEl.show();
44494                 if(c.title){
44495                     this.titleTextEl.innerHTML = c.title;
44496                 }
44497             }
44498         //}
44499         this.duration = c.duration || .30;
44500         this.slideDuration = c.slideDuration || .45;
44501         this.config = c;
44502         if(c.collapsed){
44503             this.collapse(true);
44504         }
44505         if(c.hidden){
44506             this.hide();
44507         }
44508     },
44509     /**
44510      * Returns true if this region is currently visible.
44511      * @return {Boolean}
44512      */
44513     isVisible : function(){
44514         return this.visible;
44515     },
44516
44517     /**
44518      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44519      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
44520      */
44521     setCollapsedTitle : function(title){
44522         title = title || "&#160;";
44523         if(this.collapsedTitleTextEl){
44524             this.collapsedTitleTextEl.innerHTML = title;
44525         }
44526     },
44527
44528     getBox : function(){
44529         var b;
44530         if(!this.collapsed){
44531             b = this.el.getBox(false, true);
44532         }else{
44533             b = this.collapsedEl.getBox(false, true);
44534         }
44535         return b;
44536     },
44537
44538     getMargins : function(){
44539         return this.collapsed ? this.cmargins : this.margins;
44540     },
44541
44542     highlight : function(){
44543         this.el.addClass("x-layout-panel-dragover");
44544     },
44545
44546     unhighlight : function(){
44547         this.el.removeClass("x-layout-panel-dragover");
44548     },
44549
44550     updateBox : function(box){
44551         this.box = box;
44552         if(!this.collapsed){
44553             this.el.dom.style.left = box.x + "px";
44554             this.el.dom.style.top = box.y + "px";
44555             this.updateBody(box.width, box.height);
44556         }else{
44557             this.collapsedEl.dom.style.left = box.x + "px";
44558             this.collapsedEl.dom.style.top = box.y + "px";
44559             this.collapsedEl.setSize(box.width, box.height);
44560         }
44561         if(this.tabs){
44562             this.tabs.autoSizeTabs();
44563         }
44564     },
44565
44566     updateBody : function(w, h){
44567         if(w !== null){
44568             this.el.setWidth(w);
44569             w -= this.el.getBorderWidth("rl");
44570             if(this.config.adjustments){
44571                 w += this.config.adjustments[0];
44572             }
44573         }
44574         if(h !== null){
44575             this.el.setHeight(h);
44576             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44577             h -= this.el.getBorderWidth("tb");
44578             if(this.config.adjustments){
44579                 h += this.config.adjustments[1];
44580             }
44581             this.bodyEl.setHeight(h);
44582             if(this.tabs){
44583                 h = this.tabs.syncHeight(h);
44584             }
44585         }
44586         if(this.panelSize){
44587             w = w !== null ? w : this.panelSize.width;
44588             h = h !== null ? h : this.panelSize.height;
44589         }
44590         if(this.activePanel){
44591             var el = this.activePanel.getEl();
44592             w = w !== null ? w : el.getWidth();
44593             h = h !== null ? h : el.getHeight();
44594             this.panelSize = {width: w, height: h};
44595             this.activePanel.setSize(w, h);
44596         }
44597         if(Roo.isIE && this.tabs){
44598             this.tabs.el.repaint();
44599         }
44600     },
44601
44602     /**
44603      * Returns the container element for this region.
44604      * @return {Roo.Element}
44605      */
44606     getEl : function(){
44607         return this.el;
44608     },
44609
44610     /**
44611      * Hides this region.
44612      */
44613     hide : function(){
44614         if(!this.collapsed){
44615             this.el.dom.style.left = "-2000px";
44616             this.el.hide();
44617         }else{
44618             this.collapsedEl.dom.style.left = "-2000px";
44619             this.collapsedEl.hide();
44620         }
44621         this.visible = false;
44622         this.fireEvent("visibilitychange", this, false);
44623     },
44624
44625     /**
44626      * Shows this region if it was previously hidden.
44627      */
44628     show : function(){
44629         if(!this.collapsed){
44630             this.el.show();
44631         }else{
44632             this.collapsedEl.show();
44633         }
44634         this.visible = true;
44635         this.fireEvent("visibilitychange", this, true);
44636     },
44637
44638     closeClicked : function(){
44639         if(this.activePanel){
44640             this.remove(this.activePanel);
44641         }
44642     },
44643
44644     collapseClick : function(e){
44645         if(this.isSlid){
44646            e.stopPropagation();
44647            this.slideIn();
44648         }else{
44649            e.stopPropagation();
44650            this.slideOut();
44651         }
44652     },
44653
44654     /**
44655      * Collapses this region.
44656      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44657      */
44658     collapse : function(skipAnim){
44659         if(this.collapsed) return;
44660         this.collapsed = true;
44661         if(this.split){
44662             this.split.el.hide();
44663         }
44664         if(this.config.animate && skipAnim !== true){
44665             this.fireEvent("invalidated", this);
44666             this.animateCollapse();
44667         }else{
44668             this.el.setLocation(-20000,-20000);
44669             this.el.hide();
44670             this.collapsedEl.show();
44671             this.fireEvent("collapsed", this);
44672             this.fireEvent("invalidated", this);
44673         }
44674     },
44675
44676     animateCollapse : function(){
44677         // overridden
44678     },
44679
44680     /**
44681      * Expands this region if it was previously collapsed.
44682      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44683      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44684      */
44685     expand : function(e, skipAnim){
44686         if(e) e.stopPropagation();
44687         if(!this.collapsed || this.el.hasActiveFx()) return;
44688         if(this.isSlid){
44689             this.afterSlideIn();
44690             skipAnim = true;
44691         }
44692         this.collapsed = false;
44693         if(this.config.animate && skipAnim !== true){
44694             this.animateExpand();
44695         }else{
44696             this.el.show();
44697             if(this.split){
44698                 this.split.el.show();
44699             }
44700             this.collapsedEl.setLocation(-2000,-2000);
44701             this.collapsedEl.hide();
44702             this.fireEvent("invalidated", this);
44703             this.fireEvent("expanded", this);
44704         }
44705     },
44706
44707     animateExpand : function(){
44708         // overridden
44709     },
44710
44711     initTabs : function(){
44712         this.bodyEl.setStyle("overflow", "hidden");
44713         var ts = new Roo.TabPanel(this.bodyEl.dom, {
44714             tabPosition: this.bottomTabs ? 'bottom' : 'top',
44715             disableTooltips: this.config.disableTabTips
44716         });
44717         if(this.config.hideTabs){
44718             ts.stripWrap.setDisplayed(false);
44719         }
44720         this.tabs = ts;
44721         ts.resizeTabs = this.config.resizeTabs === true;
44722         ts.minTabWidth = this.config.minTabWidth || 40;
44723         ts.maxTabWidth = this.config.maxTabWidth || 250;
44724         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44725         ts.monitorResize = false;
44726         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44727         ts.bodyEl.addClass('x-layout-tabs-body');
44728         this.panels.each(this.initPanelAsTab, this);
44729     },
44730
44731     initPanelAsTab : function(panel){
44732         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
44733                     this.config.closeOnTab && panel.isClosable());
44734         if(panel.tabTip !== undefined){
44735             ti.setTooltip(panel.tabTip);
44736         }
44737         ti.on("activate", function(){
44738               this.setActivePanel(panel);
44739         }, this);
44740         if(this.config.closeOnTab){
44741             ti.on("beforeclose", function(t, e){
44742                 e.cancel = true;
44743                 this.remove(panel);
44744             }, this);
44745         }
44746         return ti;
44747     },
44748
44749     updatePanelTitle : function(panel, title){
44750         if(this.activePanel == panel){
44751             this.updateTitle(title);
44752         }
44753         if(this.tabs){
44754             var ti = this.tabs.getTab(panel.getEl().id);
44755             ti.setText(title);
44756             if(panel.tabTip !== undefined){
44757                 ti.setTooltip(panel.tabTip);
44758             }
44759         }
44760     },
44761
44762     updateTitle : function(title){
44763         if(this.titleTextEl && !this.config.title){
44764             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44765         }
44766     },
44767
44768     setActivePanel : function(panel){
44769         panel = this.getPanel(panel);
44770         if(this.activePanel && this.activePanel != panel){
44771             this.activePanel.setActiveState(false);
44772         }
44773         this.activePanel = panel;
44774         panel.setActiveState(true);
44775         if(this.panelSize){
44776             panel.setSize(this.panelSize.width, this.panelSize.height);
44777         }
44778         if(this.closeBtn){
44779             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44780         }
44781         this.updateTitle(panel.getTitle());
44782         if(this.tabs){
44783             this.fireEvent("invalidated", this);
44784         }
44785         this.fireEvent("panelactivated", this, panel);
44786     },
44787
44788     /**
44789      * Shows the specified panel.
44790      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44791      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44792      */
44793     showPanel : function(panel){
44794         if(panel = this.getPanel(panel)){
44795             if(this.tabs){
44796                 var tab = this.tabs.getTab(panel.getEl().id);
44797                 if(tab.isHidden()){
44798                     this.tabs.unhideTab(tab.id);
44799                 }
44800                 tab.activate();
44801             }else{
44802                 this.setActivePanel(panel);
44803             }
44804         }
44805         return panel;
44806     },
44807
44808     /**
44809      * Get the active panel for this region.
44810      * @return {Roo.ContentPanel} The active panel or null
44811      */
44812     getActivePanel : function(){
44813         return this.activePanel;
44814     },
44815
44816     validateVisibility : function(){
44817         if(this.panels.getCount() < 1){
44818             this.updateTitle("&#160;");
44819             this.closeBtn.hide();
44820             this.hide();
44821         }else{
44822             if(!this.isVisible()){
44823                 this.show();
44824             }
44825         }
44826     },
44827
44828     /**
44829      * Adds the passed ContentPanel(s) to this region.
44830      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44831      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44832      */
44833     add : function(panel){
44834         if(arguments.length > 1){
44835             for(var i = 0, len = arguments.length; i < len; i++) {
44836                 this.add(arguments[i]);
44837             }
44838             return null;
44839         }
44840         if(this.hasPanel(panel)){
44841             this.showPanel(panel);
44842             return panel;
44843         }
44844         panel.setRegion(this);
44845         this.panels.add(panel);
44846         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44847             this.bodyEl.dom.appendChild(panel.getEl().dom);
44848             if(panel.background !== true){
44849                 this.setActivePanel(panel);
44850             }
44851             this.fireEvent("paneladded", this, panel);
44852             return panel;
44853         }
44854         if(!this.tabs){
44855             this.initTabs();
44856         }else{
44857             this.initPanelAsTab(panel);
44858         }
44859         if(panel.background !== true){
44860             this.tabs.activate(panel.getEl().id);
44861         }
44862         this.fireEvent("paneladded", this, panel);
44863         return panel;
44864     },
44865
44866     /**
44867      * Hides the tab for the specified panel.
44868      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44869      */
44870     hidePanel : function(panel){
44871         if(this.tabs && (panel = this.getPanel(panel))){
44872             this.tabs.hideTab(panel.getEl().id);
44873         }
44874     },
44875
44876     /**
44877      * Unhides the tab for a previously hidden panel.
44878      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44879      */
44880     unhidePanel : function(panel){
44881         if(this.tabs && (panel = this.getPanel(panel))){
44882             this.tabs.unhideTab(panel.getEl().id);
44883         }
44884     },
44885
44886     clearPanels : function(){
44887         while(this.panels.getCount() > 0){
44888              this.remove(this.panels.first());
44889         }
44890     },
44891
44892     /**
44893      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44894      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44895      * @param {Boolean} preservePanel Overrides the config preservePanel option
44896      * @return {Roo.ContentPanel} The panel that was removed
44897      */
44898     remove : function(panel, preservePanel){
44899         panel = this.getPanel(panel);
44900         if(!panel){
44901             return null;
44902         }
44903         var e = {};
44904         this.fireEvent("beforeremove", this, panel, e);
44905         if(e.cancel === true){
44906             return null;
44907         }
44908         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44909         var panelId = panel.getId();
44910         this.panels.removeKey(panelId);
44911         if(preservePanel){
44912             document.body.appendChild(panel.getEl().dom);
44913         }
44914         if(this.tabs){
44915             this.tabs.removeTab(panel.getEl().id);
44916         }else if (!preservePanel){
44917             this.bodyEl.dom.removeChild(panel.getEl().dom);
44918         }
44919         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44920             var p = this.panels.first();
44921             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44922             tempEl.appendChild(p.getEl().dom);
44923             this.bodyEl.update("");
44924             this.bodyEl.dom.appendChild(p.getEl().dom);
44925             tempEl = null;
44926             this.updateTitle(p.getTitle());
44927             this.tabs = null;
44928             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44929             this.setActivePanel(p);
44930         }
44931         panel.setRegion(null);
44932         if(this.activePanel == panel){
44933             this.activePanel = null;
44934         }
44935         if(this.config.autoDestroy !== false && preservePanel !== true){
44936             try{panel.destroy();}catch(e){}
44937         }
44938         this.fireEvent("panelremoved", this, panel);
44939         return panel;
44940     },
44941
44942     /**
44943      * Returns the TabPanel component used by this region
44944      * @return {Roo.TabPanel}
44945      */
44946     getTabs : function(){
44947         return this.tabs;
44948     },
44949
44950     createTool : function(parentEl, className){
44951         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
44952             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
44953         btn.addClassOnOver("x-layout-tools-button-over");
44954         return btn;
44955     }
44956 });/*
44957  * Based on:
44958  * Ext JS Library 1.1.1
44959  * Copyright(c) 2006-2007, Ext JS, LLC.
44960  *
44961  * Originally Released Under LGPL - original licence link has changed is not relivant.
44962  *
44963  * Fork - LGPL
44964  * <script type="text/javascript">
44965  */
44966  
44967
44968
44969 /**
44970  * @class Roo.SplitLayoutRegion
44971  * @extends Roo.LayoutRegion
44972  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44973  */
44974 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
44975     this.cursor = cursor;
44976     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
44977 };
44978
44979 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
44980     splitTip : "Drag to resize.",
44981     collapsibleSplitTip : "Drag to resize. Double click to hide.",
44982     useSplitTips : false,
44983
44984     applyConfig : function(config){
44985         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
44986         if(config.split){
44987             if(!this.split){
44988                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
44989                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
44990                 /** The SplitBar for this region 
44991                 * @type Roo.SplitBar */
44992                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
44993                 this.split.on("moved", this.onSplitMove, this);
44994                 this.split.useShim = config.useShim === true;
44995                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44996                 if(this.useSplitTips){
44997                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44998                 }
44999                 if(config.collapsible){
45000                     this.split.el.on("dblclick", this.collapse,  this);
45001                 }
45002             }
45003             if(typeof config.minSize != "undefined"){
45004                 this.split.minSize = config.minSize;
45005             }
45006             if(typeof config.maxSize != "undefined"){
45007                 this.split.maxSize = config.maxSize;
45008             }
45009             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45010                 this.hideSplitter();
45011             }
45012         }
45013     },
45014
45015     getHMaxSize : function(){
45016          var cmax = this.config.maxSize || 10000;
45017          var center = this.mgr.getRegion("center");
45018          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45019     },
45020
45021     getVMaxSize : function(){
45022          var cmax = this.config.maxSize || 10000;
45023          var center = this.mgr.getRegion("center");
45024          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45025     },
45026
45027     onSplitMove : function(split, newSize){
45028         this.fireEvent("resized", this, newSize);
45029     },
45030     
45031     /** 
45032      * Returns the {@link Roo.SplitBar} for this region.
45033      * @return {Roo.SplitBar}
45034      */
45035     getSplitBar : function(){
45036         return this.split;
45037     },
45038     
45039     hide : function(){
45040         this.hideSplitter();
45041         Roo.SplitLayoutRegion.superclass.hide.call(this);
45042     },
45043
45044     hideSplitter : function(){
45045         if(this.split){
45046             this.split.el.setLocation(-2000,-2000);
45047             this.split.el.hide();
45048         }
45049     },
45050
45051     show : function(){
45052         if(this.split){
45053             this.split.el.show();
45054         }
45055         Roo.SplitLayoutRegion.superclass.show.call(this);
45056     },
45057     
45058     beforeSlide: function(){
45059         if(Roo.isGecko){// firefox overflow auto bug workaround
45060             this.bodyEl.clip();
45061             if(this.tabs) this.tabs.bodyEl.clip();
45062             if(this.activePanel){
45063                 this.activePanel.getEl().clip();
45064                 
45065                 if(this.activePanel.beforeSlide){
45066                     this.activePanel.beforeSlide();
45067                 }
45068             }
45069         }
45070     },
45071     
45072     afterSlide : function(){
45073         if(Roo.isGecko){// firefox overflow auto bug workaround
45074             this.bodyEl.unclip();
45075             if(this.tabs) this.tabs.bodyEl.unclip();
45076             if(this.activePanel){
45077                 this.activePanel.getEl().unclip();
45078                 if(this.activePanel.afterSlide){
45079                     this.activePanel.afterSlide();
45080                 }
45081             }
45082         }
45083     },
45084
45085     initAutoHide : function(){
45086         if(this.autoHide !== false){
45087             if(!this.autoHideHd){
45088                 var st = new Roo.util.DelayedTask(this.slideIn, this);
45089                 this.autoHideHd = {
45090                     "mouseout": function(e){
45091                         if(!e.within(this.el, true)){
45092                             st.delay(500);
45093                         }
45094                     },
45095                     "mouseover" : function(e){
45096                         st.cancel();
45097                     },
45098                     scope : this
45099                 };
45100             }
45101             this.el.on(this.autoHideHd);
45102         }
45103     },
45104
45105     clearAutoHide : function(){
45106         if(this.autoHide !== false){
45107             this.el.un("mouseout", this.autoHideHd.mouseout);
45108             this.el.un("mouseover", this.autoHideHd.mouseover);
45109         }
45110     },
45111
45112     clearMonitor : function(){
45113         Roo.get(document).un("click", this.slideInIf, this);
45114     },
45115
45116     // these names are backwards but not changed for compat
45117     slideOut : function(){
45118         if(this.isSlid || this.el.hasActiveFx()){
45119             return;
45120         }
45121         this.isSlid = true;
45122         if(this.collapseBtn){
45123             this.collapseBtn.hide();
45124         }
45125         this.closeBtnState = this.closeBtn.getStyle('display');
45126         this.closeBtn.hide();
45127         if(this.stickBtn){
45128             this.stickBtn.show();
45129         }
45130         this.el.show();
45131         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
45132         this.beforeSlide();
45133         this.el.setStyle("z-index", 10001);
45134         this.el.slideIn(this.getSlideAnchor(), {
45135             callback: function(){
45136                 this.afterSlide();
45137                 this.initAutoHide();
45138                 Roo.get(document).on("click", this.slideInIf, this);
45139                 this.fireEvent("slideshow", this);
45140             },
45141             scope: this,
45142             block: true
45143         });
45144     },
45145
45146     afterSlideIn : function(){
45147         this.clearAutoHide();
45148         this.isSlid = false;
45149         this.clearMonitor();
45150         this.el.setStyle("z-index", "");
45151         if(this.collapseBtn){
45152             this.collapseBtn.show();
45153         }
45154         this.closeBtn.setStyle('display', this.closeBtnState);
45155         if(this.stickBtn){
45156             this.stickBtn.hide();
45157         }
45158         this.fireEvent("slidehide", this);
45159     },
45160
45161     slideIn : function(cb){
45162         if(!this.isSlid || this.el.hasActiveFx()){
45163             Roo.callback(cb);
45164             return;
45165         }
45166         this.isSlid = false;
45167         this.beforeSlide();
45168         this.el.slideOut(this.getSlideAnchor(), {
45169             callback: function(){
45170                 this.el.setLeftTop(-10000, -10000);
45171                 this.afterSlide();
45172                 this.afterSlideIn();
45173                 Roo.callback(cb);
45174             },
45175             scope: this,
45176             block: true
45177         });
45178     },
45179     
45180     slideInIf : function(e){
45181         if(!e.within(this.el)){
45182             this.slideIn();
45183         }
45184     },
45185
45186     animateCollapse : function(){
45187         this.beforeSlide();
45188         this.el.setStyle("z-index", 20000);
45189         var anchor = this.getSlideAnchor();
45190         this.el.slideOut(anchor, {
45191             callback : function(){
45192                 this.el.setStyle("z-index", "");
45193                 this.collapsedEl.slideIn(anchor, {duration:.3});
45194                 this.afterSlide();
45195                 this.el.setLocation(-10000,-10000);
45196                 this.el.hide();
45197                 this.fireEvent("collapsed", this);
45198             },
45199             scope: this,
45200             block: true
45201         });
45202     },
45203
45204     animateExpand : function(){
45205         this.beforeSlide();
45206         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
45207         this.el.setStyle("z-index", 20000);
45208         this.collapsedEl.hide({
45209             duration:.1
45210         });
45211         this.el.slideIn(this.getSlideAnchor(), {
45212             callback : function(){
45213                 this.el.setStyle("z-index", "");
45214                 this.afterSlide();
45215                 if(this.split){
45216                     this.split.el.show();
45217                 }
45218                 this.fireEvent("invalidated", this);
45219                 this.fireEvent("expanded", this);
45220             },
45221             scope: this,
45222             block: true
45223         });
45224     },
45225
45226     anchors : {
45227         "west" : "left",
45228         "east" : "right",
45229         "north" : "top",
45230         "south" : "bottom"
45231     },
45232
45233     sanchors : {
45234         "west" : "l",
45235         "east" : "r",
45236         "north" : "t",
45237         "south" : "b"
45238     },
45239
45240     canchors : {
45241         "west" : "tl-tr",
45242         "east" : "tr-tl",
45243         "north" : "tl-bl",
45244         "south" : "bl-tl"
45245     },
45246
45247     getAnchor : function(){
45248         return this.anchors[this.position];
45249     },
45250
45251     getCollapseAnchor : function(){
45252         return this.canchors[this.position];
45253     },
45254
45255     getSlideAnchor : function(){
45256         return this.sanchors[this.position];
45257     },
45258
45259     getAlignAdj : function(){
45260         var cm = this.cmargins;
45261         switch(this.position){
45262             case "west":
45263                 return [0, 0];
45264             break;
45265             case "east":
45266                 return [0, 0];
45267             break;
45268             case "north":
45269                 return [0, 0];
45270             break;
45271             case "south":
45272                 return [0, 0];
45273             break;
45274         }
45275     },
45276
45277     getExpandAdj : function(){
45278         var c = this.collapsedEl, cm = this.cmargins;
45279         switch(this.position){
45280             case "west":
45281                 return [-(cm.right+c.getWidth()+cm.left), 0];
45282             break;
45283             case "east":
45284                 return [cm.right+c.getWidth()+cm.left, 0];
45285             break;
45286             case "north":
45287                 return [0, -(cm.top+cm.bottom+c.getHeight())];
45288             break;
45289             case "south":
45290                 return [0, cm.top+cm.bottom+c.getHeight()];
45291             break;
45292         }
45293     }
45294 });/*
45295  * Based on:
45296  * Ext JS Library 1.1.1
45297  * Copyright(c) 2006-2007, Ext JS, LLC.
45298  *
45299  * Originally Released Under LGPL - original licence link has changed is not relivant.
45300  *
45301  * Fork - LGPL
45302  * <script type="text/javascript">
45303  */
45304 /*
45305  * These classes are private internal classes
45306  */
45307 Roo.CenterLayoutRegion = function(mgr, config){
45308     Roo.LayoutRegion.call(this, mgr, config, "center");
45309     this.visible = true;
45310     this.minWidth = config.minWidth || 20;
45311     this.minHeight = config.minHeight || 20;
45312 };
45313
45314 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
45315     hide : function(){
45316         // center panel can't be hidden
45317     },
45318     
45319     show : function(){
45320         // center panel can't be hidden
45321     },
45322     
45323     getMinWidth: function(){
45324         return this.minWidth;
45325     },
45326     
45327     getMinHeight: function(){
45328         return this.minHeight;
45329     }
45330 });
45331
45332
45333 Roo.NorthLayoutRegion = function(mgr, config){
45334     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
45335     if(this.split){
45336         this.split.placement = Roo.SplitBar.TOP;
45337         this.split.orientation = Roo.SplitBar.VERTICAL;
45338         this.split.el.addClass("x-layout-split-v");
45339     }
45340     var size = config.initialSize || config.height;
45341     if(typeof size != "undefined"){
45342         this.el.setHeight(size);
45343     }
45344 };
45345 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
45346     orientation: Roo.SplitBar.VERTICAL,
45347     getBox : function(){
45348         if(this.collapsed){
45349             return this.collapsedEl.getBox();
45350         }
45351         var box = this.el.getBox();
45352         if(this.split){
45353             box.height += this.split.el.getHeight();
45354         }
45355         return box;
45356     },
45357     
45358     updateBox : function(box){
45359         if(this.split && !this.collapsed){
45360             box.height -= this.split.el.getHeight();
45361             this.split.el.setLeft(box.x);
45362             this.split.el.setTop(box.y+box.height);
45363             this.split.el.setWidth(box.width);
45364         }
45365         if(this.collapsed){
45366             this.updateBody(box.width, null);
45367         }
45368         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45369     }
45370 });
45371
45372 Roo.SouthLayoutRegion = function(mgr, config){
45373     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
45374     if(this.split){
45375         this.split.placement = Roo.SplitBar.BOTTOM;
45376         this.split.orientation = Roo.SplitBar.VERTICAL;
45377         this.split.el.addClass("x-layout-split-v");
45378     }
45379     var size = config.initialSize || config.height;
45380     if(typeof size != "undefined"){
45381         this.el.setHeight(size);
45382     }
45383 };
45384 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
45385     orientation: Roo.SplitBar.VERTICAL,
45386     getBox : function(){
45387         if(this.collapsed){
45388             return this.collapsedEl.getBox();
45389         }
45390         var box = this.el.getBox();
45391         if(this.split){
45392             var sh = this.split.el.getHeight();
45393             box.height += sh;
45394             box.y -= sh;
45395         }
45396         return box;
45397     },
45398     
45399     updateBox : function(box){
45400         if(this.split && !this.collapsed){
45401             var sh = this.split.el.getHeight();
45402             box.height -= sh;
45403             box.y += sh;
45404             this.split.el.setLeft(box.x);
45405             this.split.el.setTop(box.y-sh);
45406             this.split.el.setWidth(box.width);
45407         }
45408         if(this.collapsed){
45409             this.updateBody(box.width, null);
45410         }
45411         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45412     }
45413 });
45414
45415 Roo.EastLayoutRegion = function(mgr, config){
45416     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
45417     if(this.split){
45418         this.split.placement = Roo.SplitBar.RIGHT;
45419         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45420         this.split.el.addClass("x-layout-split-h");
45421     }
45422     var size = config.initialSize || config.width;
45423     if(typeof size != "undefined"){
45424         this.el.setWidth(size);
45425     }
45426 };
45427 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
45428     orientation: Roo.SplitBar.HORIZONTAL,
45429     getBox : function(){
45430         if(this.collapsed){
45431             return this.collapsedEl.getBox();
45432         }
45433         var box = this.el.getBox();
45434         if(this.split){
45435             var sw = this.split.el.getWidth();
45436             box.width += sw;
45437             box.x -= sw;
45438         }
45439         return box;
45440     },
45441
45442     updateBox : function(box){
45443         if(this.split && !this.collapsed){
45444             var sw = this.split.el.getWidth();
45445             box.width -= sw;
45446             this.split.el.setLeft(box.x);
45447             this.split.el.setTop(box.y);
45448             this.split.el.setHeight(box.height);
45449             box.x += sw;
45450         }
45451         if(this.collapsed){
45452             this.updateBody(null, box.height);
45453         }
45454         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45455     }
45456 });
45457
45458 Roo.WestLayoutRegion = function(mgr, config){
45459     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
45460     if(this.split){
45461         this.split.placement = Roo.SplitBar.LEFT;
45462         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45463         this.split.el.addClass("x-layout-split-h");
45464     }
45465     var size = config.initialSize || config.width;
45466     if(typeof size != "undefined"){
45467         this.el.setWidth(size);
45468     }
45469 };
45470 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
45471     orientation: Roo.SplitBar.HORIZONTAL,
45472     getBox : function(){
45473         if(this.collapsed){
45474             return this.collapsedEl.getBox();
45475         }
45476         var box = this.el.getBox();
45477         if(this.split){
45478             box.width += this.split.el.getWidth();
45479         }
45480         return box;
45481     },
45482     
45483     updateBox : function(box){
45484         if(this.split && !this.collapsed){
45485             var sw = this.split.el.getWidth();
45486             box.width -= sw;
45487             this.split.el.setLeft(box.x+box.width);
45488             this.split.el.setTop(box.y);
45489             this.split.el.setHeight(box.height);
45490         }
45491         if(this.collapsed){
45492             this.updateBody(null, box.height);
45493         }
45494         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45495     }
45496 });
45497 /*
45498  * Based on:
45499  * Ext JS Library 1.1.1
45500  * Copyright(c) 2006-2007, Ext JS, LLC.
45501  *
45502  * Originally Released Under LGPL - original licence link has changed is not relivant.
45503  *
45504  * Fork - LGPL
45505  * <script type="text/javascript">
45506  */
45507  
45508  
45509 /*
45510  * Private internal class for reading and applying state
45511  */
45512 Roo.LayoutStateManager = function(layout){
45513      // default empty state
45514      this.state = {
45515         north: {},
45516         south: {},
45517         east: {},
45518         west: {}       
45519     };
45520 };
45521
45522 Roo.LayoutStateManager.prototype = {
45523     init : function(layout, provider){
45524         this.provider = provider;
45525         var state = provider.get(layout.id+"-layout-state");
45526         if(state){
45527             var wasUpdating = layout.isUpdating();
45528             if(!wasUpdating){
45529                 layout.beginUpdate();
45530             }
45531             for(var key in state){
45532                 if(typeof state[key] != "function"){
45533                     var rstate = state[key];
45534                     var r = layout.getRegion(key);
45535                     if(r && rstate){
45536                         if(rstate.size){
45537                             r.resizeTo(rstate.size);
45538                         }
45539                         if(rstate.collapsed == true){
45540                             r.collapse(true);
45541                         }else{
45542                             r.expand(null, true);
45543                         }
45544                     }
45545                 }
45546             }
45547             if(!wasUpdating){
45548                 layout.endUpdate();
45549             }
45550             this.state = state; 
45551         }
45552         this.layout = layout;
45553         layout.on("regionresized", this.onRegionResized, this);
45554         layout.on("regioncollapsed", this.onRegionCollapsed, this);
45555         layout.on("regionexpanded", this.onRegionExpanded, this);
45556     },
45557     
45558     storeState : function(){
45559         this.provider.set(this.layout.id+"-layout-state", this.state);
45560     },
45561     
45562     onRegionResized : function(region, newSize){
45563         this.state[region.getPosition()].size = newSize;
45564         this.storeState();
45565     },
45566     
45567     onRegionCollapsed : function(region){
45568         this.state[region.getPosition()].collapsed = true;
45569         this.storeState();
45570     },
45571     
45572     onRegionExpanded : function(region){
45573         this.state[region.getPosition()].collapsed = false;
45574         this.storeState();
45575     }
45576 };/*
45577  * Based on:
45578  * Ext JS Library 1.1.1
45579  * Copyright(c) 2006-2007, Ext JS, LLC.
45580  *
45581  * Originally Released Under LGPL - original licence link has changed is not relivant.
45582  *
45583  * Fork - LGPL
45584  * <script type="text/javascript">
45585  */
45586 /**
45587  * @class Roo.ContentPanel
45588  * @extends Roo.util.Observable
45589  * A basic ContentPanel element.
45590  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
45591  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
45592  * @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
45593  * @cfg {Boolean} closable True if the panel can be closed/removed
45594  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
45595  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45596  * @cfg {Toolbar} toolbar A toolbar for this panel
45597  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
45598  * @cfg {String} title The title for this panel
45599  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45600  * @cfg {String} url Calls {@link #setUrl} with this value
45601  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45602  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
45603  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
45604  * @constructor
45605  * Create a new ContentPanel.
45606  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
45607  * @param {String/Object} config A string to set only the title or a config object
45608  * @param {String} content (optional) Set the HTML content for this panel
45609  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
45610  */
45611 Roo.ContentPanel = function(el, config, content){
45612     
45613      
45614     /*
45615     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
45616         config = el;
45617         el = Roo.id();
45618     }
45619     if (config && config.parentLayout) { 
45620         el = config.parentLayout.el.createChild(); 
45621     }
45622     */
45623     if(el.autoCreate){ // xtype is available if this is called from factory
45624         config = el;
45625         el = Roo.id();
45626     }
45627     this.el = Roo.get(el);
45628     if(!this.el && config && config.autoCreate){
45629         if(typeof config.autoCreate == "object"){
45630             if(!config.autoCreate.id){
45631                 config.autoCreate.id = config.id||el;
45632             }
45633             this.el = Roo.DomHelper.append(document.body,
45634                         config.autoCreate, true);
45635         }else{
45636             this.el = Roo.DomHelper.append(document.body,
45637                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
45638         }
45639     }
45640     this.closable = false;
45641     this.loaded = false;
45642     this.active = false;
45643     if(typeof config == "string"){
45644         this.title = config;
45645     }else{
45646         Roo.apply(this, config);
45647     }
45648     
45649     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
45650         this.wrapEl = this.el.wrap();    
45651         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
45652         
45653     }
45654     
45655     
45656     
45657     if(this.resizeEl){
45658         this.resizeEl = Roo.get(this.resizeEl, true);
45659     }else{
45660         this.resizeEl = this.el;
45661     }
45662     this.addEvents({
45663         /**
45664          * @event activate
45665          * Fires when this panel is activated. 
45666          * @param {Roo.ContentPanel} this
45667          */
45668         "activate" : true,
45669         /**
45670          * @event deactivate
45671          * Fires when this panel is activated. 
45672          * @param {Roo.ContentPanel} this
45673          */
45674         "deactivate" : true,
45675
45676         /**
45677          * @event resize
45678          * Fires when this panel is resized if fitToFrame is true.
45679          * @param {Roo.ContentPanel} this
45680          * @param {Number} width The width after any component adjustments
45681          * @param {Number} height The height after any component adjustments
45682          */
45683         "resize" : true
45684     });
45685     if(this.autoScroll){
45686         this.resizeEl.setStyle("overflow", "auto");
45687     } else {
45688         // fix randome scrolling
45689         this.el.on('scroll', function() {
45690             Roo.log('fix random scolling');
45691             this.scrollTo('top',0); 
45692         });
45693     }
45694     content = content || this.content;
45695     if(content){
45696         this.setContent(content);
45697     }
45698     if(config && config.url){
45699         this.setUrl(this.url, this.params, this.loadOnce);
45700     }
45701     
45702     
45703     
45704     Roo.ContentPanel.superclass.constructor.call(this);
45705 };
45706
45707 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
45708     tabTip:'',
45709     setRegion : function(region){
45710         this.region = region;
45711         if(region){
45712            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
45713         }else{
45714            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
45715         } 
45716     },
45717     
45718     /**
45719      * Returns the toolbar for this Panel if one was configured. 
45720      * @return {Roo.Toolbar} 
45721      */
45722     getToolbar : function(){
45723         return this.toolbar;
45724     },
45725     
45726     setActiveState : function(active){
45727         this.active = active;
45728         if(!active){
45729             this.fireEvent("deactivate", this);
45730         }else{
45731             this.fireEvent("activate", this);
45732         }
45733     },
45734     /**
45735      * Updates this panel's element
45736      * @param {String} content The new content
45737      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45738     */
45739     setContent : function(content, loadScripts){
45740         this.el.update(content, loadScripts);
45741     },
45742
45743     ignoreResize : function(w, h){
45744         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45745             return true;
45746         }else{
45747             this.lastSize = {width: w, height: h};
45748             return false;
45749         }
45750     },
45751     /**
45752      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45753      * @return {Roo.UpdateManager} The UpdateManager
45754      */
45755     getUpdateManager : function(){
45756         return this.el.getUpdateManager();
45757     },
45758      /**
45759      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45760      * @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:
45761 <pre><code>
45762 panel.load({
45763     url: "your-url.php",
45764     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45765     callback: yourFunction,
45766     scope: yourObject, //(optional scope)
45767     discardUrl: false,
45768     nocache: false,
45769     text: "Loading...",
45770     timeout: 30,
45771     scripts: false
45772 });
45773 </code></pre>
45774      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45775      * 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.
45776      * @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}
45777      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45778      * @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.
45779      * @return {Roo.ContentPanel} this
45780      */
45781     load : function(){
45782         var um = this.el.getUpdateManager();
45783         um.update.apply(um, arguments);
45784         return this;
45785     },
45786
45787
45788     /**
45789      * 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.
45790      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45791      * @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)
45792      * @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)
45793      * @return {Roo.UpdateManager} The UpdateManager
45794      */
45795     setUrl : function(url, params, loadOnce){
45796         if(this.refreshDelegate){
45797             this.removeListener("activate", this.refreshDelegate);
45798         }
45799         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45800         this.on("activate", this.refreshDelegate);
45801         return this.el.getUpdateManager();
45802     },
45803     
45804     _handleRefresh : function(url, params, loadOnce){
45805         if(!loadOnce || !this.loaded){
45806             var updater = this.el.getUpdateManager();
45807             updater.update(url, params, this._setLoaded.createDelegate(this));
45808         }
45809     },
45810     
45811     _setLoaded : function(){
45812         this.loaded = true;
45813     }, 
45814     
45815     /**
45816      * Returns this panel's id
45817      * @return {String} 
45818      */
45819     getId : function(){
45820         return this.el.id;
45821     },
45822     
45823     /** 
45824      * Returns this panel's element - used by regiosn to add.
45825      * @return {Roo.Element} 
45826      */
45827     getEl : function(){
45828         return this.wrapEl || this.el;
45829     },
45830     
45831     adjustForComponents : function(width, height){
45832         if(this.resizeEl != this.el){
45833             width -= this.el.getFrameWidth('lr');
45834             height -= this.el.getFrameWidth('tb');
45835         }
45836         if(this.toolbar){
45837             var te = this.toolbar.getEl();
45838             height -= te.getHeight();
45839             te.setWidth(width);
45840         }
45841         if(this.adjustments){
45842             width += this.adjustments[0];
45843             height += this.adjustments[1];
45844         }
45845         return {"width": width, "height": height};
45846     },
45847     
45848     setSize : function(width, height){
45849         if(this.fitToFrame && !this.ignoreResize(width, height)){
45850             if(this.fitContainer && this.resizeEl != this.el){
45851                 this.el.setSize(width, height);
45852             }
45853             var size = this.adjustForComponents(width, height);
45854             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45855             this.fireEvent('resize', this, size.width, size.height);
45856         }
45857     },
45858     
45859     /**
45860      * Returns this panel's title
45861      * @return {String} 
45862      */
45863     getTitle : function(){
45864         return this.title;
45865     },
45866     
45867     /**
45868      * Set this panel's title
45869      * @param {String} title
45870      */
45871     setTitle : function(title){
45872         this.title = title;
45873         if(this.region){
45874             this.region.updatePanelTitle(this, title);
45875         }
45876     },
45877     
45878     /**
45879      * Returns true is this panel was configured to be closable
45880      * @return {Boolean} 
45881      */
45882     isClosable : function(){
45883         return this.closable;
45884     },
45885     
45886     beforeSlide : function(){
45887         this.el.clip();
45888         this.resizeEl.clip();
45889     },
45890     
45891     afterSlide : function(){
45892         this.el.unclip();
45893         this.resizeEl.unclip();
45894     },
45895     
45896     /**
45897      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45898      *   Will fail silently if the {@link #setUrl} method has not been called.
45899      *   This does not activate the panel, just updates its content.
45900      */
45901     refresh : function(){
45902         if(this.refreshDelegate){
45903            this.loaded = false;
45904            this.refreshDelegate();
45905         }
45906     },
45907     
45908     /**
45909      * Destroys this panel
45910      */
45911     destroy : function(){
45912         this.el.removeAllListeners();
45913         var tempEl = document.createElement("span");
45914         tempEl.appendChild(this.el.dom);
45915         tempEl.innerHTML = "";
45916         this.el.remove();
45917         this.el = null;
45918     },
45919     
45920       /**
45921      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45922      * <pre><code>
45923
45924 layout.addxtype({
45925        xtype : 'Form',
45926        items: [ .... ]
45927    }
45928 );
45929
45930 </code></pre>
45931      * @param {Object} cfg Xtype definition of item to add.
45932      */
45933     
45934     addxtype : function(cfg) {
45935         // add form..
45936         if (cfg.xtype.match(/^Form$/)) {
45937             var el = this.el.createChild();
45938
45939             this.form = new  Roo.form.Form(cfg);
45940             
45941             
45942             if ( this.form.allItems.length) this.form.render(el.dom);
45943             return this.form;
45944         }
45945         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
45946             // views..
45947             cfg.el = this.el.appendChild(document.createElement("div"));
45948             // factory?
45949             var ret = new Roo[cfg.xtype](cfg);
45950             ret.render(false, ''); // render blank..
45951             return ret;
45952             
45953         }
45954         return false;
45955         
45956     }
45957 });
45958
45959 /**
45960  * @class Roo.GridPanel
45961  * @extends Roo.ContentPanel
45962  * @constructor
45963  * Create a new GridPanel.
45964  * @param {Roo.grid.Grid} grid The grid for this panel
45965  * @param {String/Object} config A string to set only the panel's title, or a config object
45966  */
45967 Roo.GridPanel = function(grid, config){
45968     
45969   
45970     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45971         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
45972         
45973     this.wrapper.dom.appendChild(grid.getGridEl().dom);
45974     
45975     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
45976     
45977     if(this.toolbar){
45978         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
45979     }
45980     // xtype created footer. - not sure if will work as we normally have to render first..
45981     if (this.footer && !this.footer.el && this.footer.xtype) {
45982         
45983         this.footer.container = this.grid.getView().getFooterPanel(true);
45984         this.footer.dataSource = this.grid.dataSource;
45985         this.footer = Roo.factory(this.footer, Roo);
45986         
45987     }
45988     
45989     grid.monitorWindowResize = false; // turn off autosizing
45990     grid.autoHeight = false;
45991     grid.autoWidth = false;
45992     this.grid = grid;
45993     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
45994 };
45995
45996 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
45997     getId : function(){
45998         return this.grid.id;
45999     },
46000     
46001     /**
46002      * Returns the grid for this panel
46003      * @return {Roo.grid.Grid} 
46004      */
46005     getGrid : function(){
46006         return this.grid;    
46007     },
46008     
46009     setSize : function(width, height){
46010         if(!this.ignoreResize(width, height)){
46011             var grid = this.grid;
46012             var size = this.adjustForComponents(width, height);
46013             grid.getGridEl().setSize(size.width, size.height);
46014             grid.autoSize();
46015         }
46016     },
46017     
46018     beforeSlide : function(){
46019         this.grid.getView().scroller.clip();
46020     },
46021     
46022     afterSlide : function(){
46023         this.grid.getView().scroller.unclip();
46024     },
46025     
46026     destroy : function(){
46027         this.grid.destroy();
46028         delete this.grid;
46029         Roo.GridPanel.superclass.destroy.call(this); 
46030     }
46031 });
46032
46033
46034 /**
46035  * @class Roo.NestedLayoutPanel
46036  * @extends Roo.ContentPanel
46037  * @constructor
46038  * Create a new NestedLayoutPanel.
46039  * 
46040  * 
46041  * @param {Roo.BorderLayout} layout The layout for this panel
46042  * @param {String/Object} config A string to set only the title or a config object
46043  */
46044 Roo.NestedLayoutPanel = function(layout, config)
46045 {
46046     // construct with only one argument..
46047     /* FIXME - implement nicer consturctors
46048     if (layout.layout) {
46049         config = layout;
46050         layout = config.layout;
46051         delete config.layout;
46052     }
46053     if (layout.xtype && !layout.getEl) {
46054         // then layout needs constructing..
46055         layout = Roo.factory(layout, Roo);
46056     }
46057     */
46058     
46059     
46060     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
46061     
46062     layout.monitorWindowResize = false; // turn off autosizing
46063     this.layout = layout;
46064     this.layout.getEl().addClass("x-layout-nested-layout");
46065     
46066     
46067     
46068     
46069 };
46070
46071 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
46072
46073     setSize : function(width, height){
46074         if(!this.ignoreResize(width, height)){
46075             var size = this.adjustForComponents(width, height);
46076             var el = this.layout.getEl();
46077             el.setSize(size.width, size.height);
46078             var touch = el.dom.offsetWidth;
46079             this.layout.layout();
46080             // ie requires a double layout on the first pass
46081             if(Roo.isIE && !this.initialized){
46082                 this.initialized = true;
46083                 this.layout.layout();
46084             }
46085         }
46086     },
46087     
46088     // activate all subpanels if not currently active..
46089     
46090     setActiveState : function(active){
46091         this.active = active;
46092         if(!active){
46093             this.fireEvent("deactivate", this);
46094             return;
46095         }
46096         
46097         this.fireEvent("activate", this);
46098         // not sure if this should happen before or after..
46099         if (!this.layout) {
46100             return; // should not happen..
46101         }
46102         var reg = false;
46103         for (var r in this.layout.regions) {
46104             reg = this.layout.getRegion(r);
46105             if (reg.getActivePanel()) {
46106                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
46107                 reg.setActivePanel(reg.getActivePanel());
46108                 continue;
46109             }
46110             if (!reg.panels.length) {
46111                 continue;
46112             }
46113             reg.showPanel(reg.getPanel(0));
46114         }
46115         
46116         
46117         
46118         
46119     },
46120     
46121     /**
46122      * Returns the nested BorderLayout for this panel
46123      * @return {Roo.BorderLayout} 
46124      */
46125     getLayout : function(){
46126         return this.layout;
46127     },
46128     
46129      /**
46130      * Adds a xtype elements to the layout of the nested panel
46131      * <pre><code>
46132
46133 panel.addxtype({
46134        xtype : 'ContentPanel',
46135        region: 'west',
46136        items: [ .... ]
46137    }
46138 );
46139
46140 panel.addxtype({
46141         xtype : 'NestedLayoutPanel',
46142         region: 'west',
46143         layout: {
46144            center: { },
46145            west: { }   
46146         },
46147         items : [ ... list of content panels or nested layout panels.. ]
46148    }
46149 );
46150 </code></pre>
46151      * @param {Object} cfg Xtype definition of item to add.
46152      */
46153     addxtype : function(cfg) {
46154         return this.layout.addxtype(cfg);
46155     
46156     }
46157 });
46158
46159 Roo.ScrollPanel = function(el, config, content){
46160     config = config || {};
46161     config.fitToFrame = true;
46162     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
46163     
46164     this.el.dom.style.overflow = "hidden";
46165     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
46166     this.el.removeClass("x-layout-inactive-content");
46167     this.el.on("mousewheel", this.onWheel, this);
46168
46169     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
46170     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
46171     up.unselectable(); down.unselectable();
46172     up.on("click", this.scrollUp, this);
46173     down.on("click", this.scrollDown, this);
46174     up.addClassOnOver("x-scroller-btn-over");
46175     down.addClassOnOver("x-scroller-btn-over");
46176     up.addClassOnClick("x-scroller-btn-click");
46177     down.addClassOnClick("x-scroller-btn-click");
46178     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
46179
46180     this.resizeEl = this.el;
46181     this.el = wrap; this.up = up; this.down = down;
46182 };
46183
46184 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
46185     increment : 100,
46186     wheelIncrement : 5,
46187     scrollUp : function(){
46188         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
46189     },
46190
46191     scrollDown : function(){
46192         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
46193     },
46194
46195     afterScroll : function(){
46196         var el = this.resizeEl;
46197         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
46198         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
46199         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
46200     },
46201
46202     setSize : function(){
46203         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
46204         this.afterScroll();
46205     },
46206
46207     onWheel : function(e){
46208         var d = e.getWheelDelta();
46209         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
46210         this.afterScroll();
46211         e.stopEvent();
46212     },
46213
46214     setContent : function(content, loadScripts){
46215         this.resizeEl.update(content, loadScripts);
46216     }
46217
46218 });
46219
46220
46221
46222
46223
46224
46225
46226
46227
46228 /**
46229  * @class Roo.TreePanel
46230  * @extends Roo.ContentPanel
46231  * @constructor
46232  * Create a new TreePanel. - defaults to fit/scoll contents.
46233  * @param {String/Object} config A string to set only the panel's title, or a config object
46234  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
46235  */
46236 Roo.TreePanel = function(config){
46237     var el = config.el;
46238     var tree = config.tree;
46239     delete config.tree; 
46240     delete config.el; // hopefull!
46241     
46242     // wrapper for IE7 strict & safari scroll issue
46243     
46244     var treeEl = el.createChild();
46245     config.resizeEl = treeEl;
46246     
46247     
46248     
46249     Roo.TreePanel.superclass.constructor.call(this, el, config);
46250  
46251  
46252     this.tree = new Roo.tree.TreePanel(treeEl , tree);
46253     //console.log(tree);
46254     this.on('activate', function()
46255     {
46256         if (this.tree.rendered) {
46257             return;
46258         }
46259         //console.log('render tree');
46260         this.tree.render();
46261     });
46262     
46263     this.on('resize',  function (cp, w, h) {
46264             this.tree.innerCt.setWidth(w);
46265             this.tree.innerCt.setHeight(h);
46266             this.tree.innerCt.setStyle('overflow-y', 'auto');
46267     });
46268
46269         
46270     
46271 };
46272
46273 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
46274     fitToFrame : true,
46275     autoScroll : true
46276 });
46277
46278
46279
46280
46281
46282
46283
46284
46285
46286
46287
46288 /*
46289  * Based on:
46290  * Ext JS Library 1.1.1
46291  * Copyright(c) 2006-2007, Ext JS, LLC.
46292  *
46293  * Originally Released Under LGPL - original licence link has changed is not relivant.
46294  *
46295  * Fork - LGPL
46296  * <script type="text/javascript">
46297  */
46298  
46299
46300 /**
46301  * @class Roo.ReaderLayout
46302  * @extends Roo.BorderLayout
46303  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
46304  * center region containing two nested regions (a top one for a list view and one for item preview below),
46305  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
46306  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
46307  * expedites the setup of the overall layout and regions for this common application style.
46308  * Example:
46309  <pre><code>
46310 var reader = new Roo.ReaderLayout();
46311 var CP = Roo.ContentPanel;  // shortcut for adding
46312
46313 reader.beginUpdate();
46314 reader.add("north", new CP("north", "North"));
46315 reader.add("west", new CP("west", {title: "West"}));
46316 reader.add("east", new CP("east", {title: "East"}));
46317
46318 reader.regions.listView.add(new CP("listView", "List"));
46319 reader.regions.preview.add(new CP("preview", "Preview"));
46320 reader.endUpdate();
46321 </code></pre>
46322 * @constructor
46323 * Create a new ReaderLayout
46324 * @param {Object} config Configuration options
46325 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
46326 * document.body if omitted)
46327 */
46328 Roo.ReaderLayout = function(config, renderTo){
46329     var c = config || {size:{}};
46330     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
46331         north: c.north !== false ? Roo.apply({
46332             split:false,
46333             initialSize: 32,
46334             titlebar: false
46335         }, c.north) : false,
46336         west: c.west !== false ? Roo.apply({
46337             split:true,
46338             initialSize: 200,
46339             minSize: 175,
46340             maxSize: 400,
46341             titlebar: true,
46342             collapsible: true,
46343             animate: true,
46344             margins:{left:5,right:0,bottom:5,top:5},
46345             cmargins:{left:5,right:5,bottom:5,top:5}
46346         }, c.west) : false,
46347         east: c.east !== false ? Roo.apply({
46348             split:true,
46349             initialSize: 200,
46350             minSize: 175,
46351             maxSize: 400,
46352             titlebar: true,
46353             collapsible: true,
46354             animate: true,
46355             margins:{left:0,right:5,bottom:5,top:5},
46356             cmargins:{left:5,right:5,bottom:5,top:5}
46357         }, c.east) : false,
46358         center: Roo.apply({
46359             tabPosition: 'top',
46360             autoScroll:false,
46361             closeOnTab: true,
46362             titlebar:false,
46363             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
46364         }, c.center)
46365     });
46366
46367     this.el.addClass('x-reader');
46368
46369     this.beginUpdate();
46370
46371     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
46372         south: c.preview !== false ? Roo.apply({
46373             split:true,
46374             initialSize: 200,
46375             minSize: 100,
46376             autoScroll:true,
46377             collapsible:true,
46378             titlebar: true,
46379             cmargins:{top:5,left:0, right:0, bottom:0}
46380         }, c.preview) : false,
46381         center: Roo.apply({
46382             autoScroll:false,
46383             titlebar:false,
46384             minHeight:200
46385         }, c.listView)
46386     });
46387     this.add('center', new Roo.NestedLayoutPanel(inner,
46388             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
46389
46390     this.endUpdate();
46391
46392     this.regions.preview = inner.getRegion('south');
46393     this.regions.listView = inner.getRegion('center');
46394 };
46395
46396 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
46397  * Based on:
46398  * Ext JS Library 1.1.1
46399  * Copyright(c) 2006-2007, Ext JS, LLC.
46400  *
46401  * Originally Released Under LGPL - original licence link has changed is not relivant.
46402  *
46403  * Fork - LGPL
46404  * <script type="text/javascript">
46405  */
46406  
46407 /**
46408  * @class Roo.grid.Grid
46409  * @extends Roo.util.Observable
46410  * This class represents the primary interface of a component based grid control.
46411  * <br><br>Usage:<pre><code>
46412  var grid = new Roo.grid.Grid("my-container-id", {
46413      ds: myDataStore,
46414      cm: myColModel,
46415      selModel: mySelectionModel,
46416      autoSizeColumns: true,
46417      monitorWindowResize: false,
46418      trackMouseOver: true
46419  });
46420  // set any options
46421  grid.render();
46422  * </code></pre>
46423  * <b>Common Problems:</b><br/>
46424  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
46425  * element will correct this<br/>
46426  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
46427  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
46428  * are unpredictable.<br/>
46429  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
46430  * grid to calculate dimensions/offsets.<br/>
46431   * @constructor
46432  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
46433  * The container MUST have some type of size defined for the grid to fill. The container will be
46434  * automatically set to position relative if it isn't already.
46435  * @param {Object} config A config object that sets properties on this grid.
46436  */
46437 Roo.grid.Grid = function(container, config){
46438         // initialize the container
46439         this.container = Roo.get(container);
46440         this.container.update("");
46441         this.container.setStyle("overflow", "hidden");
46442     this.container.addClass('x-grid-container');
46443
46444     this.id = this.container.id;
46445
46446     Roo.apply(this, config);
46447     // check and correct shorthanded configs
46448     if(this.ds){
46449         this.dataSource = this.ds;
46450         delete this.ds;
46451     }
46452     if(this.cm){
46453         this.colModel = this.cm;
46454         delete this.cm;
46455     }
46456     if(this.sm){
46457         this.selModel = this.sm;
46458         delete this.sm;
46459     }
46460
46461     if (this.selModel) {
46462         this.selModel = Roo.factory(this.selModel, Roo.grid);
46463         this.sm = this.selModel;
46464         this.sm.xmodule = this.xmodule || false;
46465     }
46466     if (typeof(this.colModel.config) == 'undefined') {
46467         this.colModel = new Roo.grid.ColumnModel(this.colModel);
46468         this.cm = this.colModel;
46469         this.cm.xmodule = this.xmodule || false;
46470     }
46471     if (this.dataSource) {
46472         this.dataSource= Roo.factory(this.dataSource, Roo.data);
46473         this.ds = this.dataSource;
46474         this.ds.xmodule = this.xmodule || false;
46475         this.ds.multiSort = this.multiSort || false;   
46476     }
46477     
46478     
46479     
46480     if(this.width){
46481         this.container.setWidth(this.width);
46482     }
46483
46484     if(this.height){
46485         this.container.setHeight(this.height);
46486     }
46487     /** @private */
46488         this.addEvents({
46489         // raw events
46490         /**
46491          * @event click
46492          * The raw click event for the entire grid.
46493          * @param {Roo.EventObject} e
46494          */
46495         "click" : true,
46496         /**
46497          * @event dblclick
46498          * The raw dblclick event for the entire grid.
46499          * @param {Roo.EventObject} e
46500          */
46501         "dblclick" : true,
46502         /**
46503          * @event contextmenu
46504          * The raw contextmenu event for the entire grid.
46505          * @param {Roo.EventObject} e
46506          */
46507         "contextmenu" : true,
46508         /**
46509          * @event mousedown
46510          * The raw mousedown event for the entire grid.
46511          * @param {Roo.EventObject} e
46512          */
46513         "mousedown" : true,
46514         /**
46515          * @event mouseup
46516          * The raw mouseup event for the entire grid.
46517          * @param {Roo.EventObject} e
46518          */
46519         "mouseup" : true,
46520         /**
46521          * @event mouseover
46522          * The raw mouseover event for the entire grid.
46523          * @param {Roo.EventObject} e
46524          */
46525         "mouseover" : true,
46526         /**
46527          * @event mouseout
46528          * The raw mouseout event for the entire grid.
46529          * @param {Roo.EventObject} e
46530          */
46531         "mouseout" : true,
46532         /**
46533          * @event keypress
46534          * The raw keypress event for the entire grid.
46535          * @param {Roo.EventObject} e
46536          */
46537         "keypress" : true,
46538         /**
46539          * @event keydown
46540          * The raw keydown event for the entire grid.
46541          * @param {Roo.EventObject} e
46542          */
46543         "keydown" : true,
46544
46545         // custom events
46546
46547         /**
46548          * @event cellclick
46549          * Fires when a cell is clicked
46550          * @param {Grid} this
46551          * @param {Number} rowIndex
46552          * @param {Number} columnIndex
46553          * @param {Roo.EventObject} e
46554          */
46555         "cellclick" : true,
46556         /**
46557          * @event celldblclick
46558          * Fires when a cell is double clicked
46559          * @param {Grid} this
46560          * @param {Number} rowIndex
46561          * @param {Number} columnIndex
46562          * @param {Roo.EventObject} e
46563          */
46564         "celldblclick" : true,
46565         /**
46566          * @event rowclick
46567          * Fires when a row is clicked
46568          * @param {Grid} this
46569          * @param {Number} rowIndex
46570          * @param {Roo.EventObject} e
46571          */
46572         "rowclick" : true,
46573         /**
46574          * @event rowdblclick
46575          * Fires when a row is double clicked
46576          * @param {Grid} this
46577          * @param {Number} rowIndex
46578          * @param {Roo.EventObject} e
46579          */
46580         "rowdblclick" : true,
46581         /**
46582          * @event headerclick
46583          * Fires when a header is clicked
46584          * @param {Grid} this
46585          * @param {Number} columnIndex
46586          * @param {Roo.EventObject} e
46587          */
46588         "headerclick" : true,
46589         /**
46590          * @event headerdblclick
46591          * Fires when a header cell is double clicked
46592          * @param {Grid} this
46593          * @param {Number} columnIndex
46594          * @param {Roo.EventObject} e
46595          */
46596         "headerdblclick" : true,
46597         /**
46598          * @event rowcontextmenu
46599          * Fires when a row is right clicked
46600          * @param {Grid} this
46601          * @param {Number} rowIndex
46602          * @param {Roo.EventObject} e
46603          */
46604         "rowcontextmenu" : true,
46605         /**
46606          * @event cellcontextmenu
46607          * Fires when a cell is right clicked
46608          * @param {Grid} this
46609          * @param {Number} rowIndex
46610          * @param {Number} cellIndex
46611          * @param {Roo.EventObject} e
46612          */
46613          "cellcontextmenu" : true,
46614         /**
46615          * @event headercontextmenu
46616          * Fires when a header is right clicked
46617          * @param {Grid} this
46618          * @param {Number} columnIndex
46619          * @param {Roo.EventObject} e
46620          */
46621         "headercontextmenu" : true,
46622         /**
46623          * @event bodyscroll
46624          * Fires when the body element is scrolled
46625          * @param {Number} scrollLeft
46626          * @param {Number} scrollTop
46627          */
46628         "bodyscroll" : true,
46629         /**
46630          * @event columnresize
46631          * Fires when the user resizes a column
46632          * @param {Number} columnIndex
46633          * @param {Number} newSize
46634          */
46635         "columnresize" : true,
46636         /**
46637          * @event columnmove
46638          * Fires when the user moves a column
46639          * @param {Number} oldIndex
46640          * @param {Number} newIndex
46641          */
46642         "columnmove" : true,
46643         /**
46644          * @event startdrag
46645          * Fires when row(s) start being dragged
46646          * @param {Grid} this
46647          * @param {Roo.GridDD} dd The drag drop object
46648          * @param {event} e The raw browser event
46649          */
46650         "startdrag" : true,
46651         /**
46652          * @event enddrag
46653          * Fires when a drag operation is complete
46654          * @param {Grid} this
46655          * @param {Roo.GridDD} dd The drag drop object
46656          * @param {event} e The raw browser event
46657          */
46658         "enddrag" : true,
46659         /**
46660          * @event dragdrop
46661          * Fires when dragged row(s) are dropped on a valid DD target
46662          * @param {Grid} this
46663          * @param {Roo.GridDD} dd The drag drop object
46664          * @param {String} targetId The target drag drop object
46665          * @param {event} e The raw browser event
46666          */
46667         "dragdrop" : true,
46668         /**
46669          * @event dragover
46670          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
46671          * @param {Grid} this
46672          * @param {Roo.GridDD} dd The drag drop object
46673          * @param {String} targetId The target drag drop object
46674          * @param {event} e The raw browser event
46675          */
46676         "dragover" : true,
46677         /**
46678          * @event dragenter
46679          *  Fires when the dragged row(s) first cross another DD target while being dragged
46680          * @param {Grid} this
46681          * @param {Roo.GridDD} dd The drag drop object
46682          * @param {String} targetId The target drag drop object
46683          * @param {event} e The raw browser event
46684          */
46685         "dragenter" : true,
46686         /**
46687          * @event dragout
46688          * Fires when the dragged row(s) leave another DD target while being dragged
46689          * @param {Grid} this
46690          * @param {Roo.GridDD} dd The drag drop object
46691          * @param {String} targetId The target drag drop object
46692          * @param {event} e The raw browser event
46693          */
46694         "dragout" : true,
46695         /**
46696          * @event rowclass
46697          * Fires when a row is rendered, so you can change add a style to it.
46698          * @param {GridView} gridview   The grid view
46699          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
46700          */
46701         'rowclass' : true,
46702
46703         /**
46704          * @event render
46705          * Fires when the grid is rendered
46706          * @param {Grid} grid
46707          */
46708         'render' : true
46709     });
46710
46711     Roo.grid.Grid.superclass.constructor.call(this);
46712 };
46713 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
46714     
46715     /**
46716      * @cfg {String} ddGroup - drag drop group.
46717      */
46718
46719     /**
46720      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
46721      */
46722     minColumnWidth : 25,
46723
46724     /**
46725      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
46726      * <b>on initial render.</b> It is more efficient to explicitly size the columns
46727      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
46728      */
46729     autoSizeColumns : false,
46730
46731     /**
46732      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
46733      */
46734     autoSizeHeaders : true,
46735
46736     /**
46737      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
46738      */
46739     monitorWindowResize : true,
46740
46741     /**
46742      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
46743      * rows measured to get a columns size. Default is 0 (all rows).
46744      */
46745     maxRowsToMeasure : 0,
46746
46747     /**
46748      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
46749      */
46750     trackMouseOver : true,
46751
46752     /**
46753     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
46754     */
46755     
46756     /**
46757     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
46758     */
46759     enableDragDrop : false,
46760     
46761     /**
46762     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
46763     */
46764     enableColumnMove : true,
46765     
46766     /**
46767     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
46768     */
46769     enableColumnHide : true,
46770     
46771     /**
46772     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
46773     */
46774     enableRowHeightSync : false,
46775     
46776     /**
46777     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
46778     */
46779     stripeRows : true,
46780     
46781     /**
46782     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
46783     */
46784     autoHeight : false,
46785
46786     /**
46787      * @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.
46788      */
46789     autoExpandColumn : false,
46790
46791     /**
46792     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
46793     * Default is 50.
46794     */
46795     autoExpandMin : 50,
46796
46797     /**
46798     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
46799     */
46800     autoExpandMax : 1000,
46801
46802     /**
46803     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
46804     */
46805     view : null,
46806
46807     /**
46808     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
46809     */
46810     loadMask : false,
46811     /**
46812     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
46813     */
46814     dropTarget: false,
46815     
46816     /**
46817     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns)
46818     */
46819     multiSort: false,
46820     
46821     // private
46822     rendered : false,
46823
46824     /**
46825     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
46826     * of a fixed width. Default is false.
46827     */
46828     /**
46829     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
46830     */
46831     /**
46832      * Called once after all setup has been completed and the grid is ready to be rendered.
46833      * @return {Roo.grid.Grid} this
46834      */
46835     render : function()
46836     {
46837         var c = this.container;
46838         // try to detect autoHeight/width mode
46839         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
46840             this.autoHeight = true;
46841         }
46842         var view = this.getView();
46843         view.init(this);
46844
46845         c.on("click", this.onClick, this);
46846         c.on("dblclick", this.onDblClick, this);
46847         c.on("contextmenu", this.onContextMenu, this);
46848         c.on("keydown", this.onKeyDown, this);
46849
46850         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
46851
46852         this.getSelectionModel().init(this);
46853
46854         view.render();
46855
46856         if(this.loadMask){
46857             this.loadMask = new Roo.LoadMask(this.container,
46858                     Roo.apply({store:this.dataSource}, this.loadMask));
46859         }
46860         
46861         
46862         if (this.toolbar && this.toolbar.xtype) {
46863             this.toolbar.container = this.getView().getHeaderPanel(true);
46864             this.toolbar = new Roo.Toolbar(this.toolbar);
46865         }
46866         if (this.footer && this.footer.xtype) {
46867             this.footer.dataSource = this.getDataSource();
46868             this.footer.container = this.getView().getFooterPanel(true);
46869             this.footer = Roo.factory(this.footer, Roo);
46870         }
46871         if (this.dropTarget && this.dropTarget.xtype) {
46872             delete this.dropTarget.xtype;
46873             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
46874         }
46875         
46876         
46877         this.rendered = true;
46878         this.fireEvent('render', this);
46879         return this;
46880     },
46881
46882         /**
46883          * Reconfigures the grid to use a different Store and Column Model.
46884          * The View will be bound to the new objects and refreshed.
46885          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
46886          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
46887          */
46888     reconfigure : function(dataSource, colModel){
46889         if(this.loadMask){
46890             this.loadMask.destroy();
46891             this.loadMask = new Roo.LoadMask(this.container,
46892                     Roo.apply({store:dataSource}, this.loadMask));
46893         }
46894         this.view.bind(dataSource, colModel);
46895         this.dataSource = dataSource;
46896         this.colModel = colModel;
46897         this.view.refresh(true);
46898     },
46899
46900     // private
46901     onKeyDown : function(e){
46902         this.fireEvent("keydown", e);
46903     },
46904
46905     /**
46906      * Destroy this grid.
46907      * @param {Boolean} removeEl True to remove the element
46908      */
46909     destroy : function(removeEl, keepListeners){
46910         if(this.loadMask){
46911             this.loadMask.destroy();
46912         }
46913         var c = this.container;
46914         c.removeAllListeners();
46915         this.view.destroy();
46916         this.colModel.purgeListeners();
46917         if(!keepListeners){
46918             this.purgeListeners();
46919         }
46920         c.update("");
46921         if(removeEl === true){
46922             c.remove();
46923         }
46924     },
46925
46926     // private
46927     processEvent : function(name, e){
46928         this.fireEvent(name, e);
46929         var t = e.getTarget();
46930         var v = this.view;
46931         var header = v.findHeaderIndex(t);
46932         if(header !== false){
46933             this.fireEvent("header" + name, this, header, e);
46934         }else{
46935             var row = v.findRowIndex(t);
46936             var cell = v.findCellIndex(t);
46937             if(row !== false){
46938                 this.fireEvent("row" + name, this, row, e);
46939                 if(cell !== false){
46940                     this.fireEvent("cell" + name, this, row, cell, e);
46941                 }
46942             }
46943         }
46944     },
46945
46946     // private
46947     onClick : function(e){
46948         this.processEvent("click", e);
46949     },
46950
46951     // private
46952     onContextMenu : function(e, t){
46953         this.processEvent("contextmenu", e);
46954     },
46955
46956     // private
46957     onDblClick : function(e){
46958         this.processEvent("dblclick", e);
46959     },
46960
46961     // private
46962     walkCells : function(row, col, step, fn, scope){
46963         var cm = this.colModel, clen = cm.getColumnCount();
46964         var ds = this.dataSource, rlen = ds.getCount(), first = true;
46965         if(step < 0){
46966             if(col < 0){
46967                 row--;
46968                 first = false;
46969             }
46970             while(row >= 0){
46971                 if(!first){
46972                     col = clen-1;
46973                 }
46974                 first = false;
46975                 while(col >= 0){
46976                     if(fn.call(scope || this, row, col, cm) === true){
46977                         return [row, col];
46978                     }
46979                     col--;
46980                 }
46981                 row--;
46982             }
46983         } else {
46984             if(col >= clen){
46985                 row++;
46986                 first = false;
46987             }
46988             while(row < rlen){
46989                 if(!first){
46990                     col = 0;
46991                 }
46992                 first = false;
46993                 while(col < clen){
46994                     if(fn.call(scope || this, row, col, cm) === true){
46995                         return [row, col];
46996                     }
46997                     col++;
46998                 }
46999                 row++;
47000             }
47001         }
47002         return null;
47003     },
47004
47005     // private
47006     getSelections : function(){
47007         return this.selModel.getSelections();
47008     },
47009
47010     /**
47011      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
47012      * but if manual update is required this method will initiate it.
47013      */
47014     autoSize : function(){
47015         if(this.rendered){
47016             this.view.layout();
47017             if(this.view.adjustForScroll){
47018                 this.view.adjustForScroll();
47019             }
47020         }
47021     },
47022
47023     /**
47024      * Returns the grid's underlying element.
47025      * @return {Element} The element
47026      */
47027     getGridEl : function(){
47028         return this.container;
47029     },
47030
47031     // private for compatibility, overridden by editor grid
47032     stopEditing : function(){},
47033
47034     /**
47035      * Returns the grid's SelectionModel.
47036      * @return {SelectionModel}
47037      */
47038     getSelectionModel : function(){
47039         if(!this.selModel){
47040             this.selModel = new Roo.grid.RowSelectionModel();
47041         }
47042         return this.selModel;
47043     },
47044
47045     /**
47046      * Returns the grid's DataSource.
47047      * @return {DataSource}
47048      */
47049     getDataSource : function(){
47050         return this.dataSource;
47051     },
47052
47053     /**
47054      * Returns the grid's ColumnModel.
47055      * @return {ColumnModel}
47056      */
47057     getColumnModel : function(){
47058         return this.colModel;
47059     },
47060
47061     /**
47062      * Returns the grid's GridView object.
47063      * @return {GridView}
47064      */
47065     getView : function(){
47066         if(!this.view){
47067             this.view = new Roo.grid.GridView(this.viewConfig);
47068         }
47069         return this.view;
47070     },
47071     /**
47072      * Called to get grid's drag proxy text, by default returns this.ddText.
47073      * @return {String}
47074      */
47075     getDragDropText : function(){
47076         var count = this.selModel.getCount();
47077         return String.format(this.ddText, count, count == 1 ? '' : 's');
47078     }
47079 });
47080 /**
47081  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
47082  * %0 is replaced with the number of selected rows.
47083  * @type String
47084  */
47085 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
47086  * Based on:
47087  * Ext JS Library 1.1.1
47088  * Copyright(c) 2006-2007, Ext JS, LLC.
47089  *
47090  * Originally Released Under LGPL - original licence link has changed is not relivant.
47091  *
47092  * Fork - LGPL
47093  * <script type="text/javascript">
47094  */
47095  
47096 Roo.grid.AbstractGridView = function(){
47097         this.grid = null;
47098         
47099         this.events = {
47100             "beforerowremoved" : true,
47101             "beforerowsinserted" : true,
47102             "beforerefresh" : true,
47103             "rowremoved" : true,
47104             "rowsinserted" : true,
47105             "rowupdated" : true,
47106             "refresh" : true
47107         };
47108     Roo.grid.AbstractGridView.superclass.constructor.call(this);
47109 };
47110
47111 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
47112     rowClass : "x-grid-row",
47113     cellClass : "x-grid-cell",
47114     tdClass : "x-grid-td",
47115     hdClass : "x-grid-hd",
47116     splitClass : "x-grid-hd-split",
47117     
47118         init: function(grid){
47119         this.grid = grid;
47120                 var cid = this.grid.getGridEl().id;
47121         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
47122         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
47123         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
47124         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
47125         },
47126         
47127         getColumnRenderers : function(){
47128         var renderers = [];
47129         var cm = this.grid.colModel;
47130         var colCount = cm.getColumnCount();
47131         for(var i = 0; i < colCount; i++){
47132             renderers[i] = cm.getRenderer(i);
47133         }
47134         return renderers;
47135     },
47136     
47137     getColumnIds : function(){
47138         var ids = [];
47139         var cm = this.grid.colModel;
47140         var colCount = cm.getColumnCount();
47141         for(var i = 0; i < colCount; i++){
47142             ids[i] = cm.getColumnId(i);
47143         }
47144         return ids;
47145     },
47146     
47147     getDataIndexes : function(){
47148         if(!this.indexMap){
47149             this.indexMap = this.buildIndexMap();
47150         }
47151         return this.indexMap.colToData;
47152     },
47153     
47154     getColumnIndexByDataIndex : function(dataIndex){
47155         if(!this.indexMap){
47156             this.indexMap = this.buildIndexMap();
47157         }
47158         return this.indexMap.dataToCol[dataIndex];
47159     },
47160     
47161     /**
47162      * Set a css style for a column dynamically. 
47163      * @param {Number} colIndex The index of the column
47164      * @param {String} name The css property name
47165      * @param {String} value The css value
47166      */
47167     setCSSStyle : function(colIndex, name, value){
47168         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
47169         Roo.util.CSS.updateRule(selector, name, value);
47170     },
47171     
47172     generateRules : function(cm){
47173         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
47174         Roo.util.CSS.removeStyleSheet(rulesId);
47175         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47176             var cid = cm.getColumnId(i);
47177             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
47178                          this.tdSelector, cid, " {\n}\n",
47179                          this.hdSelector, cid, " {\n}\n",
47180                          this.splitSelector, cid, " {\n}\n");
47181         }
47182         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
47183     }
47184 });/*
47185  * Based on:
47186  * Ext JS Library 1.1.1
47187  * Copyright(c) 2006-2007, Ext JS, LLC.
47188  *
47189  * Originally Released Under LGPL - original licence link has changed is not relivant.
47190  *
47191  * Fork - LGPL
47192  * <script type="text/javascript">
47193  */
47194
47195 // private
47196 // This is a support class used internally by the Grid components
47197 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
47198     this.grid = grid;
47199     this.view = grid.getView();
47200     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
47201     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
47202     if(hd2){
47203         this.setHandleElId(Roo.id(hd));
47204         this.setOuterHandleElId(Roo.id(hd2));
47205     }
47206     this.scroll = false;
47207 };
47208 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
47209     maxDragWidth: 120,
47210     getDragData : function(e){
47211         var t = Roo.lib.Event.getTarget(e);
47212         var h = this.view.findHeaderCell(t);
47213         if(h){
47214             return {ddel: h.firstChild, header:h};
47215         }
47216         return false;
47217     },
47218
47219     onInitDrag : function(e){
47220         this.view.headersDisabled = true;
47221         var clone = this.dragData.ddel.cloneNode(true);
47222         clone.id = Roo.id();
47223         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
47224         this.proxy.update(clone);
47225         return true;
47226     },
47227
47228     afterValidDrop : function(){
47229         var v = this.view;
47230         setTimeout(function(){
47231             v.headersDisabled = false;
47232         }, 50);
47233     },
47234
47235     afterInvalidDrop : function(){
47236         var v = this.view;
47237         setTimeout(function(){
47238             v.headersDisabled = false;
47239         }, 50);
47240     }
47241 });
47242 /*
47243  * Based on:
47244  * Ext JS Library 1.1.1
47245  * Copyright(c) 2006-2007, Ext JS, LLC.
47246  *
47247  * Originally Released Under LGPL - original licence link has changed is not relivant.
47248  *
47249  * Fork - LGPL
47250  * <script type="text/javascript">
47251  */
47252 // private
47253 // This is a support class used internally by the Grid components
47254 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
47255     this.grid = grid;
47256     this.view = grid.getView();
47257     // split the proxies so they don't interfere with mouse events
47258     this.proxyTop = Roo.DomHelper.append(document.body, {
47259         cls:"col-move-top", html:"&#160;"
47260     }, true);
47261     this.proxyBottom = Roo.DomHelper.append(document.body, {
47262         cls:"col-move-bottom", html:"&#160;"
47263     }, true);
47264     this.proxyTop.hide = this.proxyBottom.hide = function(){
47265         this.setLeftTop(-100,-100);
47266         this.setStyle("visibility", "hidden");
47267     };
47268     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
47269     // temporarily disabled
47270     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
47271     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
47272 };
47273 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
47274     proxyOffsets : [-4, -9],
47275     fly: Roo.Element.fly,
47276
47277     getTargetFromEvent : function(e){
47278         var t = Roo.lib.Event.getTarget(e);
47279         var cindex = this.view.findCellIndex(t);
47280         if(cindex !== false){
47281             return this.view.getHeaderCell(cindex);
47282         }
47283     },
47284
47285     nextVisible : function(h){
47286         var v = this.view, cm = this.grid.colModel;
47287         h = h.nextSibling;
47288         while(h){
47289             if(!cm.isHidden(v.getCellIndex(h))){
47290                 return h;
47291             }
47292             h = h.nextSibling;
47293         }
47294         return null;
47295     },
47296
47297     prevVisible : function(h){
47298         var v = this.view, cm = this.grid.colModel;
47299         h = h.prevSibling;
47300         while(h){
47301             if(!cm.isHidden(v.getCellIndex(h))){
47302                 return h;
47303             }
47304             h = h.prevSibling;
47305         }
47306         return null;
47307     },
47308
47309     positionIndicator : function(h, n, e){
47310         var x = Roo.lib.Event.getPageX(e);
47311         var r = Roo.lib.Dom.getRegion(n.firstChild);
47312         var px, pt, py = r.top + this.proxyOffsets[1];
47313         if((r.right - x) <= (r.right-r.left)/2){
47314             px = r.right+this.view.borderWidth;
47315             pt = "after";
47316         }else{
47317             px = r.left;
47318             pt = "before";
47319         }
47320         var oldIndex = this.view.getCellIndex(h);
47321         var newIndex = this.view.getCellIndex(n);
47322
47323         if(this.grid.colModel.isFixed(newIndex)){
47324             return false;
47325         }
47326
47327         var locked = this.grid.colModel.isLocked(newIndex);
47328
47329         if(pt == "after"){
47330             newIndex++;
47331         }
47332         if(oldIndex < newIndex){
47333             newIndex--;
47334         }
47335         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
47336             return false;
47337         }
47338         px +=  this.proxyOffsets[0];
47339         this.proxyTop.setLeftTop(px, py);
47340         this.proxyTop.show();
47341         if(!this.bottomOffset){
47342             this.bottomOffset = this.view.mainHd.getHeight();
47343         }
47344         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
47345         this.proxyBottom.show();
47346         return pt;
47347     },
47348
47349     onNodeEnter : function(n, dd, e, data){
47350         if(data.header != n){
47351             this.positionIndicator(data.header, n, e);
47352         }
47353     },
47354
47355     onNodeOver : function(n, dd, e, data){
47356         var result = false;
47357         if(data.header != n){
47358             result = this.positionIndicator(data.header, n, e);
47359         }
47360         if(!result){
47361             this.proxyTop.hide();
47362             this.proxyBottom.hide();
47363         }
47364         return result ? this.dropAllowed : this.dropNotAllowed;
47365     },
47366
47367     onNodeOut : function(n, dd, e, data){
47368         this.proxyTop.hide();
47369         this.proxyBottom.hide();
47370     },
47371
47372     onNodeDrop : function(n, dd, e, data){
47373         var h = data.header;
47374         if(h != n){
47375             var cm = this.grid.colModel;
47376             var x = Roo.lib.Event.getPageX(e);
47377             var r = Roo.lib.Dom.getRegion(n.firstChild);
47378             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
47379             var oldIndex = this.view.getCellIndex(h);
47380             var newIndex = this.view.getCellIndex(n);
47381             var locked = cm.isLocked(newIndex);
47382             if(pt == "after"){
47383                 newIndex++;
47384             }
47385             if(oldIndex < newIndex){
47386                 newIndex--;
47387             }
47388             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
47389                 return false;
47390             }
47391             cm.setLocked(oldIndex, locked, true);
47392             cm.moveColumn(oldIndex, newIndex);
47393             this.grid.fireEvent("columnmove", oldIndex, newIndex);
47394             return true;
47395         }
47396         return false;
47397     }
47398 });
47399 /*
47400  * Based on:
47401  * Ext JS Library 1.1.1
47402  * Copyright(c) 2006-2007, Ext JS, LLC.
47403  *
47404  * Originally Released Under LGPL - original licence link has changed is not relivant.
47405  *
47406  * Fork - LGPL
47407  * <script type="text/javascript">
47408  */
47409   
47410 /**
47411  * @class Roo.grid.GridView
47412  * @extends Roo.util.Observable
47413  *
47414  * @constructor
47415  * @param {Object} config
47416  */
47417 Roo.grid.GridView = function(config){
47418     Roo.grid.GridView.superclass.constructor.call(this);
47419     this.el = null;
47420
47421     Roo.apply(this, config);
47422 };
47423
47424 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
47425
47426     /**
47427      * Override this function to apply custom css classes to rows during rendering
47428      * @param {Record} record The record
47429      * @param {Number} index
47430      * @method getRowClass
47431      */
47432     rowClass : "x-grid-row",
47433
47434     cellClass : "x-grid-col",
47435
47436     tdClass : "x-grid-td",
47437
47438     hdClass : "x-grid-hd",
47439
47440     splitClass : "x-grid-split",
47441
47442     sortClasses : ["sort-asc", "sort-desc"],
47443
47444     enableMoveAnim : false,
47445
47446     hlColor: "C3DAF9",
47447
47448     dh : Roo.DomHelper,
47449
47450     fly : Roo.Element.fly,
47451
47452     css : Roo.util.CSS,
47453
47454     borderWidth: 1,
47455
47456     splitOffset: 3,
47457
47458     scrollIncrement : 22,
47459
47460     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
47461
47462     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
47463
47464     bind : function(ds, cm){
47465         if(this.ds){
47466             this.ds.un("load", this.onLoad, this);
47467             this.ds.un("datachanged", this.onDataChange, this);
47468             this.ds.un("add", this.onAdd, this);
47469             this.ds.un("remove", this.onRemove, this);
47470             this.ds.un("update", this.onUpdate, this);
47471             this.ds.un("clear", this.onClear, this);
47472         }
47473         if(ds){
47474             ds.on("load", this.onLoad, this);
47475             ds.on("datachanged", this.onDataChange, this);
47476             ds.on("add", this.onAdd, this);
47477             ds.on("remove", this.onRemove, this);
47478             ds.on("update", this.onUpdate, this);
47479             ds.on("clear", this.onClear, this);
47480         }
47481         this.ds = ds;
47482
47483         if(this.cm){
47484             this.cm.un("widthchange", this.onColWidthChange, this);
47485             this.cm.un("headerchange", this.onHeaderChange, this);
47486             this.cm.un("hiddenchange", this.onHiddenChange, this);
47487             this.cm.un("columnmoved", this.onColumnMove, this);
47488             this.cm.un("columnlockchange", this.onColumnLock, this);
47489         }
47490         if(cm){
47491             this.generateRules(cm);
47492             cm.on("widthchange", this.onColWidthChange, this);
47493             cm.on("headerchange", this.onHeaderChange, this);
47494             cm.on("hiddenchange", this.onHiddenChange, this);
47495             cm.on("columnmoved", this.onColumnMove, this);
47496             cm.on("columnlockchange", this.onColumnLock, this);
47497         }
47498         this.cm = cm;
47499     },
47500
47501     init: function(grid){
47502         Roo.grid.GridView.superclass.init.call(this, grid);
47503
47504         this.bind(grid.dataSource, grid.colModel);
47505
47506         grid.on("headerclick", this.handleHeaderClick, this);
47507
47508         if(grid.trackMouseOver){
47509             grid.on("mouseover", this.onRowOver, this);
47510             grid.on("mouseout", this.onRowOut, this);
47511         }
47512         grid.cancelTextSelection = function(){};
47513         this.gridId = grid.id;
47514
47515         var tpls = this.templates || {};
47516
47517         if(!tpls.master){
47518             tpls.master = new Roo.Template(
47519                '<div class="x-grid" hidefocus="true">',
47520                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
47521                   '<div class="x-grid-topbar"></div>',
47522                   '<div class="x-grid-scroller"><div></div></div>',
47523                   '<div class="x-grid-locked">',
47524                       '<div class="x-grid-header">{lockedHeader}</div>',
47525                       '<div class="x-grid-body">{lockedBody}</div>',
47526                   "</div>",
47527                   '<div class="x-grid-viewport">',
47528                       '<div class="x-grid-header">{header}</div>',
47529                       '<div class="x-grid-body">{body}</div>',
47530                   "</div>",
47531                   '<div class="x-grid-bottombar"></div>',
47532                  
47533                   '<div class="x-grid-resize-proxy">&#160;</div>',
47534                "</div>"
47535             );
47536             tpls.master.disableformats = true;
47537         }
47538
47539         if(!tpls.header){
47540             tpls.header = new Roo.Template(
47541                '<table border="0" cellspacing="0" cellpadding="0">',
47542                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
47543                "</table>{splits}"
47544             );
47545             tpls.header.disableformats = true;
47546         }
47547         tpls.header.compile();
47548
47549         if(!tpls.hcell){
47550             tpls.hcell = new Roo.Template(
47551                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
47552                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
47553                 "</div></td>"
47554              );
47555              tpls.hcell.disableFormats = true;
47556         }
47557         tpls.hcell.compile();
47558
47559         if(!tpls.hsplit){
47560             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
47561             tpls.hsplit.disableFormats = true;
47562         }
47563         tpls.hsplit.compile();
47564
47565         if(!tpls.body){
47566             tpls.body = new Roo.Template(
47567                '<table border="0" cellspacing="0" cellpadding="0">',
47568                "<tbody>{rows}</tbody>",
47569                "</table>"
47570             );
47571             tpls.body.disableFormats = true;
47572         }
47573         tpls.body.compile();
47574
47575         if(!tpls.row){
47576             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
47577             tpls.row.disableFormats = true;
47578         }
47579         tpls.row.compile();
47580
47581         if(!tpls.cell){
47582             tpls.cell = new Roo.Template(
47583                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
47584                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
47585                 "</td>"
47586             );
47587             tpls.cell.disableFormats = true;
47588         }
47589         tpls.cell.compile();
47590
47591         this.templates = tpls;
47592     },
47593
47594     // remap these for backwards compat
47595     onColWidthChange : function(){
47596         this.updateColumns.apply(this, arguments);
47597     },
47598     onHeaderChange : function(){
47599         this.updateHeaders.apply(this, arguments);
47600     }, 
47601     onHiddenChange : function(){
47602         this.handleHiddenChange.apply(this, arguments);
47603     },
47604     onColumnMove : function(){
47605         this.handleColumnMove.apply(this, arguments);
47606     },
47607     onColumnLock : function(){
47608         this.handleLockChange.apply(this, arguments);
47609     },
47610
47611     onDataChange : function(){
47612         this.refresh();
47613         this.updateHeaderSortState();
47614     },
47615
47616     onClear : function(){
47617         this.refresh();
47618     },
47619
47620     onUpdate : function(ds, record){
47621         this.refreshRow(record);
47622     },
47623
47624     refreshRow : function(record){
47625         var ds = this.ds, index;
47626         if(typeof record == 'number'){
47627             index = record;
47628             record = ds.getAt(index);
47629         }else{
47630             index = ds.indexOf(record);
47631         }
47632         this.insertRows(ds, index, index, true);
47633         this.onRemove(ds, record, index+1, true);
47634         this.syncRowHeights(index, index);
47635         this.layout();
47636         this.fireEvent("rowupdated", this, index, record);
47637     },
47638
47639     onAdd : function(ds, records, index){
47640         this.insertRows(ds, index, index + (records.length-1));
47641     },
47642
47643     onRemove : function(ds, record, index, isUpdate){
47644         if(isUpdate !== true){
47645             this.fireEvent("beforerowremoved", this, index, record);
47646         }
47647         var bt = this.getBodyTable(), lt = this.getLockedTable();
47648         if(bt.rows[index]){
47649             bt.firstChild.removeChild(bt.rows[index]);
47650         }
47651         if(lt.rows[index]){
47652             lt.firstChild.removeChild(lt.rows[index]);
47653         }
47654         if(isUpdate !== true){
47655             this.stripeRows(index);
47656             this.syncRowHeights(index, index);
47657             this.layout();
47658             this.fireEvent("rowremoved", this, index, record);
47659         }
47660     },
47661
47662     onLoad : function(){
47663         this.scrollToTop();
47664     },
47665
47666     /**
47667      * Scrolls the grid to the top
47668      */
47669     scrollToTop : function(){
47670         if(this.scroller){
47671             this.scroller.dom.scrollTop = 0;
47672             this.syncScroll();
47673         }
47674     },
47675
47676     /**
47677      * Gets a panel in the header of the grid that can be used for toolbars etc.
47678      * After modifying the contents of this panel a call to grid.autoSize() may be
47679      * required to register any changes in size.
47680      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
47681      * @return Roo.Element
47682      */
47683     getHeaderPanel : function(doShow){
47684         if(doShow){
47685             this.headerPanel.show();
47686         }
47687         return this.headerPanel;
47688     },
47689
47690     /**
47691      * Gets a panel in the footer of the grid that can be used for toolbars etc.
47692      * After modifying the contents of this panel a call to grid.autoSize() may be
47693      * required to register any changes in size.
47694      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
47695      * @return Roo.Element
47696      */
47697     getFooterPanel : function(doShow){
47698         if(doShow){
47699             this.footerPanel.show();
47700         }
47701         return this.footerPanel;
47702     },
47703
47704     initElements : function(){
47705         var E = Roo.Element;
47706         var el = this.grid.getGridEl().dom.firstChild;
47707         var cs = el.childNodes;
47708
47709         this.el = new E(el);
47710         
47711          this.focusEl = new E(el.firstChild);
47712         this.focusEl.swallowEvent("click", true);
47713         
47714         this.headerPanel = new E(cs[1]);
47715         this.headerPanel.enableDisplayMode("block");
47716
47717         this.scroller = new E(cs[2]);
47718         this.scrollSizer = new E(this.scroller.dom.firstChild);
47719
47720         this.lockedWrap = new E(cs[3]);
47721         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
47722         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
47723
47724         this.mainWrap = new E(cs[4]);
47725         this.mainHd = new E(this.mainWrap.dom.firstChild);
47726         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
47727
47728         this.footerPanel = new E(cs[5]);
47729         this.footerPanel.enableDisplayMode("block");
47730
47731         this.resizeProxy = new E(cs[6]);
47732
47733         this.headerSelector = String.format(
47734            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
47735            this.lockedHd.id, this.mainHd.id
47736         );
47737
47738         this.splitterSelector = String.format(
47739            '#{0} div.x-grid-split, #{1} div.x-grid-split',
47740            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
47741         );
47742     },
47743     idToCssName : function(s)
47744     {
47745         return s.replace(/[^a-z0-9]+/ig, '-');
47746     },
47747
47748     getHeaderCell : function(index){
47749         return Roo.DomQuery.select(this.headerSelector)[index];
47750     },
47751
47752     getHeaderCellMeasure : function(index){
47753         return this.getHeaderCell(index).firstChild;
47754     },
47755
47756     getHeaderCellText : function(index){
47757         return this.getHeaderCell(index).firstChild.firstChild;
47758     },
47759
47760     getLockedTable : function(){
47761         return this.lockedBody.dom.firstChild;
47762     },
47763
47764     getBodyTable : function(){
47765         return this.mainBody.dom.firstChild;
47766     },
47767
47768     getLockedRow : function(index){
47769         return this.getLockedTable().rows[index];
47770     },
47771
47772     getRow : function(index){
47773         return this.getBodyTable().rows[index];
47774     },
47775
47776     getRowComposite : function(index){
47777         if(!this.rowEl){
47778             this.rowEl = new Roo.CompositeElementLite();
47779         }
47780         var els = [], lrow, mrow;
47781         if(lrow = this.getLockedRow(index)){
47782             els.push(lrow);
47783         }
47784         if(mrow = this.getRow(index)){
47785             els.push(mrow);
47786         }
47787         this.rowEl.elements = els;
47788         return this.rowEl;
47789     },
47790
47791     getCell : function(rowIndex, colIndex){
47792         var locked = this.cm.getLockedCount();
47793         var source;
47794         if(colIndex < locked){
47795             source = this.lockedBody.dom.firstChild;
47796         }else{
47797             source = this.mainBody.dom.firstChild;
47798             colIndex -= locked;
47799         }
47800         return source.rows[rowIndex].childNodes[colIndex];
47801     },
47802
47803     getCellText : function(rowIndex, colIndex){
47804         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
47805     },
47806
47807     getCellBox : function(cell){
47808         var b = this.fly(cell).getBox();
47809         if(Roo.isOpera){ // opera fails to report the Y
47810             b.y = cell.offsetTop + this.mainBody.getY();
47811         }
47812         return b;
47813     },
47814
47815     getCellIndex : function(cell){
47816         var id = String(cell.className).match(this.cellRE);
47817         if(id){
47818             return parseInt(id[1], 10);
47819         }
47820         return 0;
47821     },
47822
47823     findHeaderIndex : function(n){
47824         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47825         return r ? this.getCellIndex(r) : false;
47826     },
47827
47828     findHeaderCell : function(n){
47829         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47830         return r ? r : false;
47831     },
47832
47833     findRowIndex : function(n){
47834         if(!n){
47835             return false;
47836         }
47837         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
47838         return r ? r.rowIndex : false;
47839     },
47840
47841     findCellIndex : function(node){
47842         var stop = this.el.dom;
47843         while(node && node != stop){
47844             if(this.findRE.test(node.className)){
47845                 return this.getCellIndex(node);
47846             }
47847             node = node.parentNode;
47848         }
47849         return false;
47850     },
47851
47852     getColumnId : function(index){
47853         return this.cm.getColumnId(index);
47854     },
47855
47856     getSplitters : function()
47857     {
47858         if(this.splitterSelector){
47859            return Roo.DomQuery.select(this.splitterSelector);
47860         }else{
47861             return null;
47862       }
47863     },
47864
47865     getSplitter : function(index){
47866         return this.getSplitters()[index];
47867     },
47868
47869     onRowOver : function(e, t){
47870         var row;
47871         if((row = this.findRowIndex(t)) !== false){
47872             this.getRowComposite(row).addClass("x-grid-row-over");
47873         }
47874     },
47875
47876     onRowOut : function(e, t){
47877         var row;
47878         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
47879             this.getRowComposite(row).removeClass("x-grid-row-over");
47880         }
47881     },
47882
47883     renderHeaders : function(){
47884         var cm = this.cm;
47885         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
47886         var cb = [], lb = [], sb = [], lsb = [], p = {};
47887         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47888             p.cellId = "x-grid-hd-0-" + i;
47889             p.splitId = "x-grid-csplit-0-" + i;
47890             p.id = cm.getColumnId(i);
47891             p.title = cm.getColumnTooltip(i) || "";
47892             p.value = cm.getColumnHeader(i) || "";
47893             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
47894             if(!cm.isLocked(i)){
47895                 cb[cb.length] = ct.apply(p);
47896                 sb[sb.length] = st.apply(p);
47897             }else{
47898                 lb[lb.length] = ct.apply(p);
47899                 lsb[lsb.length] = st.apply(p);
47900             }
47901         }
47902         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
47903                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
47904     },
47905
47906     updateHeaders : function(){
47907         var html = this.renderHeaders();
47908         this.lockedHd.update(html[0]);
47909         this.mainHd.update(html[1]);
47910     },
47911
47912     /**
47913      * Focuses the specified row.
47914      * @param {Number} row The row index
47915      */
47916     focusRow : function(row)
47917     {
47918         //Roo.log('GridView.focusRow');
47919         var x = this.scroller.dom.scrollLeft;
47920         this.focusCell(row, 0, false);
47921         this.scroller.dom.scrollLeft = x;
47922     },
47923
47924     /**
47925      * Focuses the specified cell.
47926      * @param {Number} row The row index
47927      * @param {Number} col The column index
47928      * @param {Boolean} hscroll false to disable horizontal scrolling
47929      */
47930     focusCell : function(row, col, hscroll)
47931     {
47932         //Roo.log('GridView.focusCell');
47933         var el = this.ensureVisible(row, col, hscroll);
47934         this.focusEl.alignTo(el, "tl-tl");
47935         if(Roo.isGecko){
47936             this.focusEl.focus();
47937         }else{
47938             this.focusEl.focus.defer(1, this.focusEl);
47939         }
47940     },
47941
47942     /**
47943      * Scrolls the specified cell into view
47944      * @param {Number} row The row index
47945      * @param {Number} col The column index
47946      * @param {Boolean} hscroll false to disable horizontal scrolling
47947      */
47948     ensureVisible : function(row, col, hscroll)
47949     {
47950         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
47951         //return null; //disable for testing.
47952         if(typeof row != "number"){
47953             row = row.rowIndex;
47954         }
47955         if(row < 0 && row >= this.ds.getCount()){
47956             return  null;
47957         }
47958         col = (col !== undefined ? col : 0);
47959         var cm = this.grid.colModel;
47960         while(cm.isHidden(col)){
47961             col++;
47962         }
47963
47964         var el = this.getCell(row, col);
47965         if(!el){
47966             return null;
47967         }
47968         var c = this.scroller.dom;
47969
47970         var ctop = parseInt(el.offsetTop, 10);
47971         var cleft = parseInt(el.offsetLeft, 10);
47972         var cbot = ctop + el.offsetHeight;
47973         var cright = cleft + el.offsetWidth;
47974         
47975         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
47976         var stop = parseInt(c.scrollTop, 10);
47977         var sleft = parseInt(c.scrollLeft, 10);
47978         var sbot = stop + ch;
47979         var sright = sleft + c.clientWidth;
47980         /*
47981         Roo.log('GridView.ensureVisible:' +
47982                 ' ctop:' + ctop +
47983                 ' c.clientHeight:' + c.clientHeight +
47984                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
47985                 ' stop:' + stop +
47986                 ' cbot:' + cbot +
47987                 ' sbot:' + sbot +
47988                 ' ch:' + ch  
47989                 );
47990         */
47991         if(ctop < stop){
47992              c.scrollTop = ctop;
47993             //Roo.log("set scrolltop to ctop DISABLE?");
47994         }else if(cbot > sbot){
47995             //Roo.log("set scrolltop to cbot-ch");
47996             c.scrollTop = cbot-ch;
47997         }
47998         
47999         if(hscroll !== false){
48000             if(cleft < sleft){
48001                 c.scrollLeft = cleft;
48002             }else if(cright > sright){
48003                 c.scrollLeft = cright-c.clientWidth;
48004             }
48005         }
48006          
48007         return el;
48008     },
48009
48010     updateColumns : function(){
48011         this.grid.stopEditing();
48012         var cm = this.grid.colModel, colIds = this.getColumnIds();
48013         //var totalWidth = cm.getTotalWidth();
48014         var pos = 0;
48015         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48016             //if(cm.isHidden(i)) continue;
48017             var w = cm.getColumnWidth(i);
48018             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48019             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48020         }
48021         this.updateSplitters();
48022     },
48023
48024     generateRules : function(cm){
48025         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
48026         Roo.util.CSS.removeStyleSheet(rulesId);
48027         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48028             var cid = cm.getColumnId(i);
48029             var align = '';
48030             if(cm.config[i].align){
48031                 align = 'text-align:'+cm.config[i].align+';';
48032             }
48033             var hidden = '';
48034             if(cm.isHidden(i)){
48035                 hidden = 'display:none;';
48036             }
48037             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
48038             ruleBuf.push(
48039                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
48040                     this.hdSelector, cid, " {\n", align, width, "}\n",
48041                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
48042                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
48043         }
48044         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48045     },
48046
48047     updateSplitters : function(){
48048         var cm = this.cm, s = this.getSplitters();
48049         if(s){ // splitters not created yet
48050             var pos = 0, locked = true;
48051             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48052                 if(cm.isHidden(i)) continue;
48053                 var w = cm.getColumnWidth(i); // make sure it's a number
48054                 if(!cm.isLocked(i) && locked){
48055                     pos = 0;
48056                     locked = false;
48057                 }
48058                 pos += w;
48059                 s[i].style.left = (pos-this.splitOffset) + "px";
48060             }
48061         }
48062     },
48063
48064     handleHiddenChange : function(colModel, colIndex, hidden){
48065         if(hidden){
48066             this.hideColumn(colIndex);
48067         }else{
48068             this.unhideColumn(colIndex);
48069         }
48070     },
48071
48072     hideColumn : function(colIndex){
48073         var cid = this.getColumnId(colIndex);
48074         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
48075         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
48076         if(Roo.isSafari){
48077             this.updateHeaders();
48078         }
48079         this.updateSplitters();
48080         this.layout();
48081     },
48082
48083     unhideColumn : function(colIndex){
48084         var cid = this.getColumnId(colIndex);
48085         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
48086         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
48087
48088         if(Roo.isSafari){
48089             this.updateHeaders();
48090         }
48091         this.updateSplitters();
48092         this.layout();
48093     },
48094
48095     insertRows : function(dm, firstRow, lastRow, isUpdate){
48096         if(firstRow == 0 && lastRow == dm.getCount()-1){
48097             this.refresh();
48098         }else{
48099             if(!isUpdate){
48100                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
48101             }
48102             var s = this.getScrollState();
48103             var markup = this.renderRows(firstRow, lastRow);
48104             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
48105             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
48106             this.restoreScroll(s);
48107             if(!isUpdate){
48108                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
48109                 this.syncRowHeights(firstRow, lastRow);
48110                 this.stripeRows(firstRow);
48111                 this.layout();
48112             }
48113         }
48114     },
48115
48116     bufferRows : function(markup, target, index){
48117         var before = null, trows = target.rows, tbody = target.tBodies[0];
48118         if(index < trows.length){
48119             before = trows[index];
48120         }
48121         var b = document.createElement("div");
48122         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
48123         var rows = b.firstChild.rows;
48124         for(var i = 0, len = rows.length; i < len; i++){
48125             if(before){
48126                 tbody.insertBefore(rows[0], before);
48127             }else{
48128                 tbody.appendChild(rows[0]);
48129             }
48130         }
48131         b.innerHTML = "";
48132         b = null;
48133     },
48134
48135     deleteRows : function(dm, firstRow, lastRow){
48136         if(dm.getRowCount()<1){
48137             this.fireEvent("beforerefresh", this);
48138             this.mainBody.update("");
48139             this.lockedBody.update("");
48140             this.fireEvent("refresh", this);
48141         }else{
48142             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
48143             var bt = this.getBodyTable();
48144             var tbody = bt.firstChild;
48145             var rows = bt.rows;
48146             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
48147                 tbody.removeChild(rows[firstRow]);
48148             }
48149             this.stripeRows(firstRow);
48150             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
48151         }
48152     },
48153
48154     updateRows : function(dataSource, firstRow, lastRow){
48155         var s = this.getScrollState();
48156         this.refresh();
48157         this.restoreScroll(s);
48158     },
48159
48160     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
48161         if(!noRefresh){
48162            this.refresh();
48163         }
48164         this.updateHeaderSortState();
48165     },
48166
48167     getScrollState : function(){
48168         
48169         var sb = this.scroller.dom;
48170         return {left: sb.scrollLeft, top: sb.scrollTop};
48171     },
48172
48173     stripeRows : function(startRow){
48174         if(!this.grid.stripeRows || this.ds.getCount() < 1){
48175             return;
48176         }
48177         startRow = startRow || 0;
48178         var rows = this.getBodyTable().rows;
48179         var lrows = this.getLockedTable().rows;
48180         var cls = ' x-grid-row-alt ';
48181         for(var i = startRow, len = rows.length; i < len; i++){
48182             var row = rows[i], lrow = lrows[i];
48183             var isAlt = ((i+1) % 2 == 0);
48184             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
48185             if(isAlt == hasAlt){
48186                 continue;
48187             }
48188             if(isAlt){
48189                 row.className += " x-grid-row-alt";
48190             }else{
48191                 row.className = row.className.replace("x-grid-row-alt", "");
48192             }
48193             if(lrow){
48194                 lrow.className = row.className;
48195             }
48196         }
48197     },
48198
48199     restoreScroll : function(state){
48200         //Roo.log('GridView.restoreScroll');
48201         var sb = this.scroller.dom;
48202         sb.scrollLeft = state.left;
48203         sb.scrollTop = state.top;
48204         this.syncScroll();
48205     },
48206
48207     syncScroll : function(){
48208         //Roo.log('GridView.syncScroll');
48209         var sb = this.scroller.dom;
48210         var sh = this.mainHd.dom;
48211         var bs = this.mainBody.dom;
48212         var lv = this.lockedBody.dom;
48213         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
48214         lv.scrollTop = bs.scrollTop = sb.scrollTop;
48215     },
48216
48217     handleScroll : function(e){
48218         this.syncScroll();
48219         var sb = this.scroller.dom;
48220         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
48221         e.stopEvent();
48222     },
48223
48224     handleWheel : function(e){
48225         var d = e.getWheelDelta();
48226         this.scroller.dom.scrollTop -= d*22;
48227         // set this here to prevent jumpy scrolling on large tables
48228         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
48229         e.stopEvent();
48230     },
48231
48232     renderRows : function(startRow, endRow){
48233         // pull in all the crap needed to render rows
48234         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
48235         var colCount = cm.getColumnCount();
48236
48237         if(ds.getCount() < 1){
48238             return ["", ""];
48239         }
48240
48241         // build a map for all the columns
48242         var cs = [];
48243         for(var i = 0; i < colCount; i++){
48244             var name = cm.getDataIndex(i);
48245             cs[i] = {
48246                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
48247                 renderer : cm.getRenderer(i),
48248                 id : cm.getColumnId(i),
48249                 locked : cm.isLocked(i)
48250             };
48251         }
48252
48253         startRow = startRow || 0;
48254         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
48255
48256         // records to render
48257         var rs = ds.getRange(startRow, endRow);
48258
48259         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
48260     },
48261
48262     // As much as I hate to duplicate code, this was branched because FireFox really hates
48263     // [].join("") on strings. The performance difference was substantial enough to
48264     // branch this function
48265     doRender : Roo.isGecko ?
48266             function(cs, rs, ds, startRow, colCount, stripe){
48267                 var ts = this.templates, ct = ts.cell, rt = ts.row;
48268                 // buffers
48269                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
48270                 
48271                 var hasListener = this.grid.hasListener('rowclass');
48272                 var rowcfg = {};
48273                 for(var j = 0, len = rs.length; j < len; j++){
48274                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
48275                     for(var i = 0; i < colCount; i++){
48276                         c = cs[i];
48277                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
48278                         p.id = c.id;
48279                         p.css = p.attr = "";
48280                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
48281                         if(p.value == undefined || p.value === "") p.value = "&#160;";
48282                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
48283                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
48284                         }
48285                         var markup = ct.apply(p);
48286                         if(!c.locked){
48287                             cb+= markup;
48288                         }else{
48289                             lcb+= markup;
48290                         }
48291                     }
48292                     var alt = [];
48293                     if(stripe && ((rowIndex+1) % 2 == 0)){
48294                         alt.push("x-grid-row-alt")
48295                     }
48296                     if(r.dirty){
48297                         alt.push(  " x-grid-dirty-row");
48298                     }
48299                     rp.cells = lcb;
48300                     if(this.getRowClass){
48301                         alt.push(this.getRowClass(r, rowIndex));
48302                     }
48303                     if (hasListener) {
48304                         rowcfg = {
48305                              
48306                             record: r,
48307                             rowIndex : rowIndex,
48308                             rowClass : ''
48309                         }
48310                         this.grid.fireEvent('rowclass', this, rowcfg);
48311                         alt.push(rowcfg.rowClass);
48312                     }
48313                     rp.alt = alt.join(" ");
48314                     lbuf+= rt.apply(rp);
48315                     rp.cells = cb;
48316                     buf+=  rt.apply(rp);
48317                 }
48318                 return [lbuf, buf];
48319             } :
48320             function(cs, rs, ds, startRow, colCount, stripe){
48321                 var ts = this.templates, ct = ts.cell, rt = ts.row;
48322                 // buffers
48323                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
48324                 var hasListener = this.grid.hasListener('rowclass');
48325                 var rowcfg = {};
48326                 for(var j = 0, len = rs.length; j < len; j++){
48327                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
48328                     for(var i = 0; i < colCount; i++){
48329                         c = cs[i];
48330                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
48331                         p.id = c.id;
48332                         p.css = p.attr = "";
48333                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
48334                         if(p.value == undefined || p.value === "") p.value = "&#160;";
48335                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
48336                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
48337                         }
48338                         var markup = ct.apply(p);
48339                         if(!c.locked){
48340                             cb[cb.length] = markup;
48341                         }else{
48342                             lcb[lcb.length] = markup;
48343                         }
48344                     }
48345                     var alt = [];
48346                     if(stripe && ((rowIndex+1) % 2 == 0)){
48347                         alt.push( "x-grid-row-alt");
48348                     }
48349                     if(r.dirty){
48350                         alt.push(" x-grid-dirty-row");
48351                     }
48352                     rp.cells = lcb;
48353                     if(this.getRowClass){
48354                         alt.push( this.getRowClass(r, rowIndex));
48355                     }
48356                     if (hasListener) {
48357                         rowcfg = {
48358                              
48359                             record: r,
48360                             rowIndex : rowIndex,
48361                             rowClass : ''
48362                         }
48363                         this.grid.fireEvent('rowclass', this, rowcfg);
48364                         alt.push(rowcfg.rowClass);
48365                     }
48366                     rp.alt = alt.join(" ");
48367                     rp.cells = lcb.join("");
48368                     lbuf[lbuf.length] = rt.apply(rp);
48369                     rp.cells = cb.join("");
48370                     buf[buf.length] =  rt.apply(rp);
48371                 }
48372                 return [lbuf.join(""), buf.join("")];
48373             },
48374
48375     renderBody : function(){
48376         var markup = this.renderRows();
48377         var bt = this.templates.body;
48378         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
48379     },
48380
48381     /**
48382      * Refreshes the grid
48383      * @param {Boolean} headersToo
48384      */
48385     refresh : function(headersToo){
48386         this.fireEvent("beforerefresh", this);
48387         this.grid.stopEditing();
48388         var result = this.renderBody();
48389         this.lockedBody.update(result[0]);
48390         this.mainBody.update(result[1]);
48391         if(headersToo === true){
48392             this.updateHeaders();
48393             this.updateColumns();
48394             this.updateSplitters();
48395             this.updateHeaderSortState();
48396         }
48397         this.syncRowHeights();
48398         this.layout();
48399         this.fireEvent("refresh", this);
48400     },
48401
48402     handleColumnMove : function(cm, oldIndex, newIndex){
48403         this.indexMap = null;
48404         var s = this.getScrollState();
48405         this.refresh(true);
48406         this.restoreScroll(s);
48407         this.afterMove(newIndex);
48408     },
48409
48410     afterMove : function(colIndex){
48411         if(this.enableMoveAnim && Roo.enableFx){
48412             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
48413         }
48414     },
48415
48416     updateCell : function(dm, rowIndex, dataIndex){
48417         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
48418         if(typeof colIndex == "undefined"){ // not present in grid
48419             return;
48420         }
48421         var cm = this.grid.colModel;
48422         var cell = this.getCell(rowIndex, colIndex);
48423         var cellText = this.getCellText(rowIndex, colIndex);
48424
48425         var p = {
48426             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
48427             id : cm.getColumnId(colIndex),
48428             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
48429         };
48430         var renderer = cm.getRenderer(colIndex);
48431         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
48432         if(typeof val == "undefined" || val === "") val = "&#160;";
48433         cellText.innerHTML = val;
48434         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
48435         this.syncRowHeights(rowIndex, rowIndex);
48436     },
48437
48438     calcColumnWidth : function(colIndex, maxRowsToMeasure){
48439         var maxWidth = 0;
48440         if(this.grid.autoSizeHeaders){
48441             var h = this.getHeaderCellMeasure(colIndex);
48442             maxWidth = Math.max(maxWidth, h.scrollWidth);
48443         }
48444         var tb, index;
48445         if(this.cm.isLocked(colIndex)){
48446             tb = this.getLockedTable();
48447             index = colIndex;
48448         }else{
48449             tb = this.getBodyTable();
48450             index = colIndex - this.cm.getLockedCount();
48451         }
48452         if(tb && tb.rows){
48453             var rows = tb.rows;
48454             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
48455             for(var i = 0; i < stopIndex; i++){
48456                 var cell = rows[i].childNodes[index].firstChild;
48457                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
48458             }
48459         }
48460         return maxWidth + /*margin for error in IE*/ 5;
48461     },
48462     /**
48463      * Autofit a column to its content.
48464      * @param {Number} colIndex
48465      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
48466      */
48467      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
48468          if(this.cm.isHidden(colIndex)){
48469              return; // can't calc a hidden column
48470          }
48471         if(forceMinSize){
48472             var cid = this.cm.getColumnId(colIndex);
48473             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
48474            if(this.grid.autoSizeHeaders){
48475                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
48476            }
48477         }
48478         var newWidth = this.calcColumnWidth(colIndex);
48479         this.cm.setColumnWidth(colIndex,
48480             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
48481         if(!suppressEvent){
48482             this.grid.fireEvent("columnresize", colIndex, newWidth);
48483         }
48484     },
48485
48486     /**
48487      * Autofits all columns to their content and then expands to fit any extra space in the grid
48488      */
48489      autoSizeColumns : function(){
48490         var cm = this.grid.colModel;
48491         var colCount = cm.getColumnCount();
48492         for(var i = 0; i < colCount; i++){
48493             this.autoSizeColumn(i, true, true);
48494         }
48495         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
48496             this.fitColumns();
48497         }else{
48498             this.updateColumns();
48499             this.layout();
48500         }
48501     },
48502
48503     /**
48504      * Autofits all columns to the grid's width proportionate with their current size
48505      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
48506      */
48507     fitColumns : function(reserveScrollSpace){
48508         var cm = this.grid.colModel;
48509         var colCount = cm.getColumnCount();
48510         var cols = [];
48511         var width = 0;
48512         var i, w;
48513         for (i = 0; i < colCount; i++){
48514             if(!cm.isHidden(i) && !cm.isFixed(i)){
48515                 w = cm.getColumnWidth(i);
48516                 cols.push(i);
48517                 cols.push(w);
48518                 width += w;
48519             }
48520         }
48521         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
48522         if(reserveScrollSpace){
48523             avail -= 17;
48524         }
48525         var frac = (avail - cm.getTotalWidth())/width;
48526         while (cols.length){
48527             w = cols.pop();
48528             i = cols.pop();
48529             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
48530         }
48531         this.updateColumns();
48532         this.layout();
48533     },
48534
48535     onRowSelect : function(rowIndex){
48536         var row = this.getRowComposite(rowIndex);
48537         row.addClass("x-grid-row-selected");
48538     },
48539
48540     onRowDeselect : function(rowIndex){
48541         var row = this.getRowComposite(rowIndex);
48542         row.removeClass("x-grid-row-selected");
48543     },
48544
48545     onCellSelect : function(row, col){
48546         var cell = this.getCell(row, col);
48547         if(cell){
48548             Roo.fly(cell).addClass("x-grid-cell-selected");
48549         }
48550     },
48551
48552     onCellDeselect : function(row, col){
48553         var cell = this.getCell(row, col);
48554         if(cell){
48555             Roo.fly(cell).removeClass("x-grid-cell-selected");
48556         }
48557     },
48558
48559     updateHeaderSortState : function(){
48560         
48561         // sort state can be single { field: xxx, direction : yyy}
48562         // or   { xxx=>ASC , yyy : DESC ..... }
48563         
48564         var mstate = {};
48565         if (!this.ds.multiSort) { 
48566             var state = this.ds.getSortState();
48567             if(!state){
48568                 return;
48569             }
48570             mstate[state.field] = state.direction;
48571             // FIXME... - this is not used here.. but might be elsewhere..
48572             this.sortState = state;
48573             
48574         } else {
48575             mstate = this.ds.sortToggle;
48576         }
48577         //remove existing sort classes..
48578         
48579         var sc = this.sortClasses;
48580         var hds = this.el.select(this.headerSelector).removeClass(sc);
48581         
48582         for(var f in mstate) {
48583         
48584             var sortColumn = this.cm.findColumnIndex(f);
48585             
48586             if(sortColumn != -1){
48587                 var sortDir = mstate[f];        
48588                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
48589             }
48590         }
48591     },
48592
48593     handleHeaderClick : function(g, index){
48594         if(this.headersDisabled){
48595             return;
48596         }
48597         var dm = g.dataSource, cm = g.colModel;
48598         if(!cm.isSortable(index)){
48599             return;
48600         }
48601         g.stopEditing();
48602         dm.sort(cm.getDataIndex(index));
48603     },
48604
48605
48606     destroy : function(){
48607         if(this.colMenu){
48608             this.colMenu.removeAll();
48609             Roo.menu.MenuMgr.unregister(this.colMenu);
48610             this.colMenu.getEl().remove();
48611             delete this.colMenu;
48612         }
48613         if(this.hmenu){
48614             this.hmenu.removeAll();
48615             Roo.menu.MenuMgr.unregister(this.hmenu);
48616             this.hmenu.getEl().remove();
48617             delete this.hmenu;
48618         }
48619         if(this.grid.enableColumnMove){
48620             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48621             if(dds){
48622                 for(var dd in dds){
48623                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
48624                         var elid = dds[dd].dragElId;
48625                         dds[dd].unreg();
48626                         Roo.get(elid).remove();
48627                     } else if(dds[dd].config.isTarget){
48628                         dds[dd].proxyTop.remove();
48629                         dds[dd].proxyBottom.remove();
48630                         dds[dd].unreg();
48631                     }
48632                     if(Roo.dd.DDM.locationCache[dd]){
48633                         delete Roo.dd.DDM.locationCache[dd];
48634                     }
48635                 }
48636                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48637             }
48638         }
48639         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
48640         this.bind(null, null);
48641         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
48642     },
48643
48644     handleLockChange : function(){
48645         this.refresh(true);
48646     },
48647
48648     onDenyColumnLock : function(){
48649
48650     },
48651
48652     onDenyColumnHide : function(){
48653
48654     },
48655
48656     handleHdMenuClick : function(item){
48657         var index = this.hdCtxIndex;
48658         var cm = this.cm, ds = this.ds;
48659         switch(item.id){
48660             case "asc":
48661                 ds.sort(cm.getDataIndex(index), "ASC");
48662                 break;
48663             case "desc":
48664                 ds.sort(cm.getDataIndex(index), "DESC");
48665                 break;
48666             case "lock":
48667                 var lc = cm.getLockedCount();
48668                 if(cm.getColumnCount(true) <= lc+1){
48669                     this.onDenyColumnLock();
48670                     return;
48671                 }
48672                 if(lc != index){
48673                     cm.setLocked(index, true, true);
48674                     cm.moveColumn(index, lc);
48675                     this.grid.fireEvent("columnmove", index, lc);
48676                 }else{
48677                     cm.setLocked(index, true);
48678                 }
48679             break;
48680             case "unlock":
48681                 var lc = cm.getLockedCount();
48682                 if((lc-1) != index){
48683                     cm.setLocked(index, false, true);
48684                     cm.moveColumn(index, lc-1);
48685                     this.grid.fireEvent("columnmove", index, lc-1);
48686                 }else{
48687                     cm.setLocked(index, false);
48688                 }
48689             break;
48690             default:
48691                 index = cm.getIndexById(item.id.substr(4));
48692                 if(index != -1){
48693                     if(item.checked && cm.getColumnCount(true) <= 1){
48694                         this.onDenyColumnHide();
48695                         return false;
48696                     }
48697                     cm.setHidden(index, item.checked);
48698                 }
48699         }
48700         return true;
48701     },
48702
48703     beforeColMenuShow : function(){
48704         var cm = this.cm,  colCount = cm.getColumnCount();
48705         this.colMenu.removeAll();
48706         for(var i = 0; i < colCount; i++){
48707             this.colMenu.add(new Roo.menu.CheckItem({
48708                 id: "col-"+cm.getColumnId(i),
48709                 text: cm.getColumnHeader(i),
48710                 checked: !cm.isHidden(i),
48711                 hideOnClick:false
48712             }));
48713         }
48714     },
48715
48716     handleHdCtx : function(g, index, e){
48717         e.stopEvent();
48718         var hd = this.getHeaderCell(index);
48719         this.hdCtxIndex = index;
48720         var ms = this.hmenu.items, cm = this.cm;
48721         ms.get("asc").setDisabled(!cm.isSortable(index));
48722         ms.get("desc").setDisabled(!cm.isSortable(index));
48723         if(this.grid.enableColLock !== false){
48724             ms.get("lock").setDisabled(cm.isLocked(index));
48725             ms.get("unlock").setDisabled(!cm.isLocked(index));
48726         }
48727         this.hmenu.show(hd, "tl-bl");
48728     },
48729
48730     handleHdOver : function(e){
48731         var hd = this.findHeaderCell(e.getTarget());
48732         if(hd && !this.headersDisabled){
48733             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
48734                this.fly(hd).addClass("x-grid-hd-over");
48735             }
48736         }
48737     },
48738
48739     handleHdOut : function(e){
48740         var hd = this.findHeaderCell(e.getTarget());
48741         if(hd){
48742             this.fly(hd).removeClass("x-grid-hd-over");
48743         }
48744     },
48745
48746     handleSplitDblClick : function(e, t){
48747         var i = this.getCellIndex(t);
48748         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
48749             this.autoSizeColumn(i, true);
48750             this.layout();
48751         }
48752     },
48753
48754     render : function(){
48755
48756         var cm = this.cm;
48757         var colCount = cm.getColumnCount();
48758
48759         if(this.grid.monitorWindowResize === true){
48760             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48761         }
48762         var header = this.renderHeaders();
48763         var body = this.templates.body.apply({rows:""});
48764         var html = this.templates.master.apply({
48765             lockedBody: body,
48766             body: body,
48767             lockedHeader: header[0],
48768             header: header[1]
48769         });
48770
48771         //this.updateColumns();
48772
48773         this.grid.getGridEl().dom.innerHTML = html;
48774
48775         this.initElements();
48776         
48777         // a kludge to fix the random scolling effect in webkit
48778         this.el.on("scroll", function() {
48779             this.el.dom.scrollTop=0; // hopefully not recursive..
48780         },this);
48781
48782         this.scroller.on("scroll", this.handleScroll, this);
48783         this.lockedBody.on("mousewheel", this.handleWheel, this);
48784         this.mainBody.on("mousewheel", this.handleWheel, this);
48785
48786         this.mainHd.on("mouseover", this.handleHdOver, this);
48787         this.mainHd.on("mouseout", this.handleHdOut, this);
48788         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
48789                 {delegate: "."+this.splitClass});
48790
48791         this.lockedHd.on("mouseover", this.handleHdOver, this);
48792         this.lockedHd.on("mouseout", this.handleHdOut, this);
48793         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
48794                 {delegate: "."+this.splitClass});
48795
48796         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
48797             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48798         }
48799
48800         this.updateSplitters();
48801
48802         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
48803             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48804             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48805         }
48806
48807         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
48808             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
48809             this.hmenu.add(
48810                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
48811                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
48812             );
48813             if(this.grid.enableColLock !== false){
48814                 this.hmenu.add('-',
48815                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
48816                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
48817                 );
48818             }
48819             if(this.grid.enableColumnHide !== false){
48820
48821                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
48822                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
48823                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
48824
48825                 this.hmenu.add('-',
48826                     {id:"columns", text: this.columnsText, menu: this.colMenu}
48827                 );
48828             }
48829             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
48830
48831             this.grid.on("headercontextmenu", this.handleHdCtx, this);
48832         }
48833
48834         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
48835             this.dd = new Roo.grid.GridDragZone(this.grid, {
48836                 ddGroup : this.grid.ddGroup || 'GridDD'
48837             });
48838         }
48839
48840         /*
48841         for(var i = 0; i < colCount; i++){
48842             if(cm.isHidden(i)){
48843                 this.hideColumn(i);
48844             }
48845             if(cm.config[i].align){
48846                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
48847                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
48848             }
48849         }*/
48850         
48851         this.updateHeaderSortState();
48852
48853         this.beforeInitialResize();
48854         this.layout(true);
48855
48856         // two part rendering gives faster view to the user
48857         this.renderPhase2.defer(1, this);
48858     },
48859
48860     renderPhase2 : function(){
48861         // render the rows now
48862         this.refresh();
48863         if(this.grid.autoSizeColumns){
48864             this.autoSizeColumns();
48865         }
48866     },
48867
48868     beforeInitialResize : function(){
48869
48870     },
48871
48872     onColumnSplitterMoved : function(i, w){
48873         this.userResized = true;
48874         var cm = this.grid.colModel;
48875         cm.setColumnWidth(i, w, true);
48876         var cid = cm.getColumnId(i);
48877         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48878         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48879         this.updateSplitters();
48880         this.layout();
48881         this.grid.fireEvent("columnresize", i, w);
48882     },
48883
48884     syncRowHeights : function(startIndex, endIndex){
48885         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
48886             startIndex = startIndex || 0;
48887             var mrows = this.getBodyTable().rows;
48888             var lrows = this.getLockedTable().rows;
48889             var len = mrows.length-1;
48890             endIndex = Math.min(endIndex || len, len);
48891             for(var i = startIndex; i <= endIndex; i++){
48892                 var m = mrows[i], l = lrows[i];
48893                 var h = Math.max(m.offsetHeight, l.offsetHeight);
48894                 m.style.height = l.style.height = h + "px";
48895             }
48896         }
48897     },
48898
48899     layout : function(initialRender, is2ndPass){
48900         var g = this.grid;
48901         var auto = g.autoHeight;
48902         var scrollOffset = 16;
48903         var c = g.getGridEl(), cm = this.cm,
48904                 expandCol = g.autoExpandColumn,
48905                 gv = this;
48906         //c.beginMeasure();
48907
48908         if(!c.dom.offsetWidth){ // display:none?
48909             if(initialRender){
48910                 this.lockedWrap.show();
48911                 this.mainWrap.show();
48912             }
48913             return;
48914         }
48915
48916         var hasLock = this.cm.isLocked(0);
48917
48918         var tbh = this.headerPanel.getHeight();
48919         var bbh = this.footerPanel.getHeight();
48920
48921         if(auto){
48922             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
48923             var newHeight = ch + c.getBorderWidth("tb");
48924             if(g.maxHeight){
48925                 newHeight = Math.min(g.maxHeight, newHeight);
48926             }
48927             c.setHeight(newHeight);
48928         }
48929
48930         if(g.autoWidth){
48931             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
48932         }
48933
48934         var s = this.scroller;
48935
48936         var csize = c.getSize(true);
48937
48938         this.el.setSize(csize.width, csize.height);
48939
48940         this.headerPanel.setWidth(csize.width);
48941         this.footerPanel.setWidth(csize.width);
48942
48943         var hdHeight = this.mainHd.getHeight();
48944         var vw = csize.width;
48945         var vh = csize.height - (tbh + bbh);
48946
48947         s.setSize(vw, vh);
48948
48949         var bt = this.getBodyTable();
48950         var ltWidth = hasLock ?
48951                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
48952
48953         var scrollHeight = bt.offsetHeight;
48954         var scrollWidth = ltWidth + bt.offsetWidth;
48955         var vscroll = false, hscroll = false;
48956
48957         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
48958
48959         var lw = this.lockedWrap, mw = this.mainWrap;
48960         var lb = this.lockedBody, mb = this.mainBody;
48961
48962         setTimeout(function(){
48963             var t = s.dom.offsetTop;
48964             var w = s.dom.clientWidth,
48965                 h = s.dom.clientHeight;
48966
48967             lw.setTop(t);
48968             lw.setSize(ltWidth, h);
48969
48970             mw.setLeftTop(ltWidth, t);
48971             mw.setSize(w-ltWidth, h);
48972
48973             lb.setHeight(h-hdHeight);
48974             mb.setHeight(h-hdHeight);
48975
48976             if(is2ndPass !== true && !gv.userResized && expandCol){
48977                 // high speed resize without full column calculation
48978                 
48979                 var ci = cm.getIndexById(expandCol);
48980                 if (ci < 0) {
48981                     ci = cm.findColumnIndex(expandCol);
48982                 }
48983                 ci = Math.max(0, ci); // make sure it's got at least the first col.
48984                 var expandId = cm.getColumnId(ci);
48985                 var  tw = cm.getTotalWidth(false);
48986                 var currentWidth = cm.getColumnWidth(ci);
48987                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
48988                 if(currentWidth != cw){
48989                     cm.setColumnWidth(ci, cw, true);
48990                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
48991                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
48992                     gv.updateSplitters();
48993                     gv.layout(false, true);
48994                 }
48995             }
48996
48997             if(initialRender){
48998                 lw.show();
48999                 mw.show();
49000             }
49001             //c.endMeasure();
49002         }, 10);
49003     },
49004
49005     onWindowResize : function(){
49006         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
49007             return;
49008         }
49009         this.layout();
49010     },
49011
49012     appendFooter : function(parentEl){
49013         return null;
49014     },
49015
49016     sortAscText : "Sort Ascending",
49017     sortDescText : "Sort Descending",
49018     lockText : "Lock Column",
49019     unlockText : "Unlock Column",
49020     columnsText : "Columns"
49021 });
49022
49023
49024 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
49025     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
49026     this.proxy.el.addClass('x-grid3-col-dd');
49027 };
49028
49029 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
49030     handleMouseDown : function(e){
49031
49032     },
49033
49034     callHandleMouseDown : function(e){
49035         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
49036     }
49037 });
49038 /*
49039  * Based on:
49040  * Ext JS Library 1.1.1
49041  * Copyright(c) 2006-2007, Ext JS, LLC.
49042  *
49043  * Originally Released Under LGPL - original licence link has changed is not relivant.
49044  *
49045  * Fork - LGPL
49046  * <script type="text/javascript">
49047  */
49048  
49049 // private
49050 // This is a support class used internally by the Grid components
49051 Roo.grid.SplitDragZone = function(grid, hd, hd2){
49052     this.grid = grid;
49053     this.view = grid.getView();
49054     this.proxy = this.view.resizeProxy;
49055     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
49056         "gridSplitters" + this.grid.getGridEl().id, {
49057         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
49058     });
49059     this.setHandleElId(Roo.id(hd));
49060     this.setOuterHandleElId(Roo.id(hd2));
49061     this.scroll = false;
49062 };
49063 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
49064     fly: Roo.Element.fly,
49065
49066     b4StartDrag : function(x, y){
49067         this.view.headersDisabled = true;
49068         this.proxy.setHeight(this.view.mainWrap.getHeight());
49069         var w = this.cm.getColumnWidth(this.cellIndex);
49070         var minw = Math.max(w-this.grid.minColumnWidth, 0);
49071         this.resetConstraints();
49072         this.setXConstraint(minw, 1000);
49073         this.setYConstraint(0, 0);
49074         this.minX = x - minw;
49075         this.maxX = x + 1000;
49076         this.startPos = x;
49077         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
49078     },
49079
49080
49081     handleMouseDown : function(e){
49082         ev = Roo.EventObject.setEvent(e);
49083         var t = this.fly(ev.getTarget());
49084         if(t.hasClass("x-grid-split")){
49085             this.cellIndex = this.view.getCellIndex(t.dom);
49086             this.split = t.dom;
49087             this.cm = this.grid.colModel;
49088             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
49089                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
49090             }
49091         }
49092     },
49093
49094     endDrag : function(e){
49095         this.view.headersDisabled = false;
49096         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
49097         var diff = endX - this.startPos;
49098         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
49099     },
49100
49101     autoOffset : function(){
49102         this.setDelta(0,0);
49103     }
49104 });/*
49105  * Based on:
49106  * Ext JS Library 1.1.1
49107  * Copyright(c) 2006-2007, Ext JS, LLC.
49108  *
49109  * Originally Released Under LGPL - original licence link has changed is not relivant.
49110  *
49111  * Fork - LGPL
49112  * <script type="text/javascript">
49113  */
49114  
49115 // private
49116 // This is a support class used internally by the Grid components
49117 Roo.grid.GridDragZone = function(grid, config){
49118     this.view = grid.getView();
49119     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
49120     if(this.view.lockedBody){
49121         this.setHandleElId(Roo.id(this.view.mainBody.dom));
49122         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
49123     }
49124     this.scroll = false;
49125     this.grid = grid;
49126     this.ddel = document.createElement('div');
49127     this.ddel.className = 'x-grid-dd-wrap';
49128 };
49129
49130 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
49131     ddGroup : "GridDD",
49132
49133     getDragData : function(e){
49134         var t = Roo.lib.Event.getTarget(e);
49135         var rowIndex = this.view.findRowIndex(t);
49136         if(rowIndex !== false){
49137             var sm = this.grid.selModel;
49138             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
49139               //  sm.mouseDown(e, t);
49140             //}
49141             if (e.hasModifier()){
49142                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
49143             }
49144             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
49145         }
49146         return false;
49147     },
49148
49149     onInitDrag : function(e){
49150         var data = this.dragData;
49151         this.ddel.innerHTML = this.grid.getDragDropText();
49152         this.proxy.update(this.ddel);
49153         // fire start drag?
49154     },
49155
49156     afterRepair : function(){
49157         this.dragging = false;
49158     },
49159
49160     getRepairXY : function(e, data){
49161         return false;
49162     },
49163
49164     onEndDrag : function(data, e){
49165         // fire end drag?
49166     },
49167
49168     onValidDrop : function(dd, e, id){
49169         // fire drag drop?
49170         this.hideProxy();
49171     },
49172
49173     beforeInvalidDrop : function(e, id){
49174
49175     }
49176 });/*
49177  * Based on:
49178  * Ext JS Library 1.1.1
49179  * Copyright(c) 2006-2007, Ext JS, LLC.
49180  *
49181  * Originally Released Under LGPL - original licence link has changed is not relivant.
49182  *
49183  * Fork - LGPL
49184  * <script type="text/javascript">
49185  */
49186  
49187
49188 /**
49189  * @class Roo.grid.ColumnModel
49190  * @extends Roo.util.Observable
49191  * This is the default implementation of a ColumnModel used by the Grid. It defines
49192  * the columns in the grid.
49193  * <br>Usage:<br>
49194  <pre><code>
49195  var colModel = new Roo.grid.ColumnModel([
49196         {header: "Ticker", width: 60, sortable: true, locked: true},
49197         {header: "Company Name", width: 150, sortable: true},
49198         {header: "Market Cap.", width: 100, sortable: true},
49199         {header: "$ Sales", width: 100, sortable: true, renderer: money},
49200         {header: "Employees", width: 100, sortable: true, resizable: false}
49201  ]);
49202  </code></pre>
49203  * <p>
49204  
49205  * The config options listed for this class are options which may appear in each
49206  * individual column definition.
49207  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
49208  * @constructor
49209  * @param {Object} config An Array of column config objects. See this class's
49210  * config objects for details.
49211 */
49212 Roo.grid.ColumnModel = function(config){
49213         /**
49214      * The config passed into the constructor
49215      */
49216     this.config = config;
49217     this.lookup = {};
49218
49219     // if no id, create one
49220     // if the column does not have a dataIndex mapping,
49221     // map it to the order it is in the config
49222     for(var i = 0, len = config.length; i < len; i++){
49223         var c = config[i];
49224         if(typeof c.dataIndex == "undefined"){
49225             c.dataIndex = i;
49226         }
49227         if(typeof c.renderer == "string"){
49228             c.renderer = Roo.util.Format[c.renderer];
49229         }
49230         if(typeof c.id == "undefined"){
49231             c.id = Roo.id();
49232         }
49233         if(c.editor && c.editor.xtype){
49234             c.editor  = Roo.factory(c.editor, Roo.grid);
49235         }
49236         if(c.editor && c.editor.isFormField){
49237             c.editor = new Roo.grid.GridEditor(c.editor);
49238         }
49239         this.lookup[c.id] = c;
49240     }
49241
49242     /**
49243      * The width of columns which have no width specified (defaults to 100)
49244      * @type Number
49245      */
49246     this.defaultWidth = 100;
49247
49248     /**
49249      * Default sortable of columns which have no sortable specified (defaults to false)
49250      * @type Boolean
49251      */
49252     this.defaultSortable = false;
49253
49254     this.addEvents({
49255         /**
49256              * @event widthchange
49257              * Fires when the width of a column changes.
49258              * @param {ColumnModel} this
49259              * @param {Number} columnIndex The column index
49260              * @param {Number} newWidth The new width
49261              */
49262             "widthchange": true,
49263         /**
49264              * @event headerchange
49265              * Fires when the text of a header changes.
49266              * @param {ColumnModel} this
49267              * @param {Number} columnIndex The column index
49268              * @param {Number} newText The new header text
49269              */
49270             "headerchange": true,
49271         /**
49272              * @event hiddenchange
49273              * Fires when a column is hidden or "unhidden".
49274              * @param {ColumnModel} this
49275              * @param {Number} columnIndex The column index
49276              * @param {Boolean} hidden true if hidden, false otherwise
49277              */
49278             "hiddenchange": true,
49279             /**
49280          * @event columnmoved
49281          * Fires when a column is moved.
49282          * @param {ColumnModel} this
49283          * @param {Number} oldIndex
49284          * @param {Number} newIndex
49285          */
49286         "columnmoved" : true,
49287         /**
49288          * @event columlockchange
49289          * Fires when a column's locked state is changed
49290          * @param {ColumnModel} this
49291          * @param {Number} colIndex
49292          * @param {Boolean} locked true if locked
49293          */
49294         "columnlockchange" : true
49295     });
49296     Roo.grid.ColumnModel.superclass.constructor.call(this);
49297 };
49298 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
49299     /**
49300      * @cfg {String} header The header text to display in the Grid view.
49301      */
49302     /**
49303      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
49304      * {@link Roo.data.Record} definition from which to draw the column's value. If not
49305      * specified, the column's index is used as an index into the Record's data Array.
49306      */
49307     /**
49308      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
49309      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
49310      */
49311     /**
49312      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
49313      * Defaults to the value of the {@link #defaultSortable} property.
49314      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
49315      */
49316     /**
49317      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
49318      */
49319     /**
49320      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
49321      */
49322     /**
49323      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
49324      */
49325     /**
49326      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
49327      */
49328     /**
49329      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
49330      * given the cell's data value. See {@link #setRenderer}. If not specified, the
49331      * default renderer uses the raw data value.
49332      */
49333        /**
49334      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
49335      */
49336     /**
49337      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
49338      */
49339
49340     /**
49341      * Returns the id of the column at the specified index.
49342      * @param {Number} index The column index
49343      * @return {String} the id
49344      */
49345     getColumnId : function(index){
49346         return this.config[index].id;
49347     },
49348
49349     /**
49350      * Returns the column for a specified id.
49351      * @param {String} id The column id
49352      * @return {Object} the column
49353      */
49354     getColumnById : function(id){
49355         return this.lookup[id];
49356     },
49357
49358     
49359     /**
49360      * Returns the column for a specified dataIndex.
49361      * @param {String} dataIndex The column dataIndex
49362      * @return {Object|Boolean} the column or false if not found
49363      */
49364     getColumnByDataIndex: function(dataIndex){
49365         var index = this.findColumnIndex(dataIndex);
49366         return index > -1 ? this.config[index] : false;
49367     },
49368     
49369     /**
49370      * Returns the index for a specified column id.
49371      * @param {String} id The column id
49372      * @return {Number} the index, or -1 if not found
49373      */
49374     getIndexById : function(id){
49375         for(var i = 0, len = this.config.length; i < len; i++){
49376             if(this.config[i].id == id){
49377                 return i;
49378             }
49379         }
49380         return -1;
49381     },
49382     
49383     /**
49384      * Returns the index for a specified column dataIndex.
49385      * @param {String} dataIndex The column dataIndex
49386      * @return {Number} the index, or -1 if not found
49387      */
49388     
49389     findColumnIndex : function(dataIndex){
49390         for(var i = 0, len = this.config.length; i < len; i++){
49391             if(this.config[i].dataIndex == dataIndex){
49392                 return i;
49393             }
49394         }
49395         return -1;
49396     },
49397     
49398     
49399     moveColumn : function(oldIndex, newIndex){
49400         var c = this.config[oldIndex];
49401         this.config.splice(oldIndex, 1);
49402         this.config.splice(newIndex, 0, c);
49403         this.dataMap = null;
49404         this.fireEvent("columnmoved", this, oldIndex, newIndex);
49405     },
49406
49407     isLocked : function(colIndex){
49408         return this.config[colIndex].locked === true;
49409     },
49410
49411     setLocked : function(colIndex, value, suppressEvent){
49412         if(this.isLocked(colIndex) == value){
49413             return;
49414         }
49415         this.config[colIndex].locked = value;
49416         if(!suppressEvent){
49417             this.fireEvent("columnlockchange", this, colIndex, value);
49418         }
49419     },
49420
49421     getTotalLockedWidth : function(){
49422         var totalWidth = 0;
49423         for(var i = 0; i < this.config.length; i++){
49424             if(this.isLocked(i) && !this.isHidden(i)){
49425                 this.totalWidth += this.getColumnWidth(i);
49426             }
49427         }
49428         return totalWidth;
49429     },
49430
49431     getLockedCount : function(){
49432         for(var i = 0, len = this.config.length; i < len; i++){
49433             if(!this.isLocked(i)){
49434                 return i;
49435             }
49436         }
49437     },
49438
49439     /**
49440      * Returns the number of columns.
49441      * @return {Number}
49442      */
49443     getColumnCount : function(visibleOnly){
49444         if(visibleOnly === true){
49445             var c = 0;
49446             for(var i = 0, len = this.config.length; i < len; i++){
49447                 if(!this.isHidden(i)){
49448                     c++;
49449                 }
49450             }
49451             return c;
49452         }
49453         return this.config.length;
49454     },
49455
49456     /**
49457      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
49458      * @param {Function} fn
49459      * @param {Object} scope (optional)
49460      * @return {Array} result
49461      */
49462     getColumnsBy : function(fn, scope){
49463         var r = [];
49464         for(var i = 0, len = this.config.length; i < len; i++){
49465             var c = this.config[i];
49466             if(fn.call(scope||this, c, i) === true){
49467                 r[r.length] = c;
49468             }
49469         }
49470         return r;
49471     },
49472
49473     /**
49474      * Returns true if the specified column is sortable.
49475      * @param {Number} col The column index
49476      * @return {Boolean}
49477      */
49478     isSortable : function(col){
49479         if(typeof this.config[col].sortable == "undefined"){
49480             return this.defaultSortable;
49481         }
49482         return this.config[col].sortable;
49483     },
49484
49485     /**
49486      * Returns the rendering (formatting) function defined for the column.
49487      * @param {Number} col The column index.
49488      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
49489      */
49490     getRenderer : function(col){
49491         if(!this.config[col].renderer){
49492             return Roo.grid.ColumnModel.defaultRenderer;
49493         }
49494         return this.config[col].renderer;
49495     },
49496
49497     /**
49498      * Sets the rendering (formatting) function for a column.
49499      * @param {Number} col The column index
49500      * @param {Function} fn The function to use to process the cell's raw data
49501      * to return HTML markup for the grid view. The render function is called with
49502      * the following parameters:<ul>
49503      * <li>Data value.</li>
49504      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
49505      * <li>css A CSS style string to apply to the table cell.</li>
49506      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
49507      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
49508      * <li>Row index</li>
49509      * <li>Column index</li>
49510      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
49511      */
49512     setRenderer : function(col, fn){
49513         this.config[col].renderer = fn;
49514     },
49515
49516     /**
49517      * Returns the width for the specified column.
49518      * @param {Number} col The column index
49519      * @return {Number}
49520      */
49521     getColumnWidth : function(col){
49522         return this.config[col].width * 1 || this.defaultWidth;
49523     },
49524
49525     /**
49526      * Sets the width for a column.
49527      * @param {Number} col The column index
49528      * @param {Number} width The new width
49529      */
49530     setColumnWidth : function(col, width, suppressEvent){
49531         this.config[col].width = width;
49532         this.totalWidth = null;
49533         if(!suppressEvent){
49534              this.fireEvent("widthchange", this, col, width);
49535         }
49536     },
49537
49538     /**
49539      * Returns the total width of all columns.
49540      * @param {Boolean} includeHidden True to include hidden column widths
49541      * @return {Number}
49542      */
49543     getTotalWidth : function(includeHidden){
49544         if(!this.totalWidth){
49545             this.totalWidth = 0;
49546             for(var i = 0, len = this.config.length; i < len; i++){
49547                 if(includeHidden || !this.isHidden(i)){
49548                     this.totalWidth += this.getColumnWidth(i);
49549                 }
49550             }
49551         }
49552         return this.totalWidth;
49553     },
49554
49555     /**
49556      * Returns the header for the specified column.
49557      * @param {Number} col The column index
49558      * @return {String}
49559      */
49560     getColumnHeader : function(col){
49561         return this.config[col].header;
49562     },
49563
49564     /**
49565      * Sets the header for a column.
49566      * @param {Number} col The column index
49567      * @param {String} header The new header
49568      */
49569     setColumnHeader : function(col, header){
49570         this.config[col].header = header;
49571         this.fireEvent("headerchange", this, col, header);
49572     },
49573
49574     /**
49575      * Returns the tooltip for the specified column.
49576      * @param {Number} col The column index
49577      * @return {String}
49578      */
49579     getColumnTooltip : function(col){
49580             return this.config[col].tooltip;
49581     },
49582     /**
49583      * Sets the tooltip for a column.
49584      * @param {Number} col The column index
49585      * @param {String} tooltip The new tooltip
49586      */
49587     setColumnTooltip : function(col, tooltip){
49588             this.config[col].tooltip = tooltip;
49589     },
49590
49591     /**
49592      * Returns the dataIndex for the specified column.
49593      * @param {Number} col The column index
49594      * @return {Number}
49595      */
49596     getDataIndex : function(col){
49597         return this.config[col].dataIndex;
49598     },
49599
49600     /**
49601      * Sets the dataIndex for a column.
49602      * @param {Number} col The column index
49603      * @param {Number} dataIndex The new dataIndex
49604      */
49605     setDataIndex : function(col, dataIndex){
49606         this.config[col].dataIndex = dataIndex;
49607     },
49608
49609     
49610     
49611     /**
49612      * Returns true if the cell is editable.
49613      * @param {Number} colIndex The column index
49614      * @param {Number} rowIndex The row index
49615      * @return {Boolean}
49616      */
49617     isCellEditable : function(colIndex, rowIndex){
49618         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
49619     },
49620
49621     /**
49622      * Returns the editor defined for the cell/column.
49623      * return false or null to disable editing.
49624      * @param {Number} colIndex The column index
49625      * @param {Number} rowIndex The row index
49626      * @return {Object}
49627      */
49628     getCellEditor : function(colIndex, rowIndex){
49629         return this.config[colIndex].editor;
49630     },
49631
49632     /**
49633      * Sets if a column is editable.
49634      * @param {Number} col The column index
49635      * @param {Boolean} editable True if the column is editable
49636      */
49637     setEditable : function(col, editable){
49638         this.config[col].editable = editable;
49639     },
49640
49641
49642     /**
49643      * Returns true if the column is hidden.
49644      * @param {Number} colIndex The column index
49645      * @return {Boolean}
49646      */
49647     isHidden : function(colIndex){
49648         return this.config[colIndex].hidden;
49649     },
49650
49651
49652     /**
49653      * Returns true if the column width cannot be changed
49654      */
49655     isFixed : function(colIndex){
49656         return this.config[colIndex].fixed;
49657     },
49658
49659     /**
49660      * Returns true if the column can be resized
49661      * @return {Boolean}
49662      */
49663     isResizable : function(colIndex){
49664         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
49665     },
49666     /**
49667      * Sets if a column is hidden.
49668      * @param {Number} colIndex The column index
49669      * @param {Boolean} hidden True if the column is hidden
49670      */
49671     setHidden : function(colIndex, hidden){
49672         this.config[colIndex].hidden = hidden;
49673         this.totalWidth = null;
49674         this.fireEvent("hiddenchange", this, colIndex, hidden);
49675     },
49676
49677     /**
49678      * Sets the editor for a column.
49679      * @param {Number} col The column index
49680      * @param {Object} editor The editor object
49681      */
49682     setEditor : function(col, editor){
49683         this.config[col].editor = editor;
49684     }
49685 });
49686
49687 Roo.grid.ColumnModel.defaultRenderer = function(value){
49688         if(typeof value == "string" && value.length < 1){
49689             return "&#160;";
49690         }
49691         return value;
49692 };
49693
49694 // Alias for backwards compatibility
49695 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
49696 /*
49697  * Based on:
49698  * Ext JS Library 1.1.1
49699  * Copyright(c) 2006-2007, Ext JS, LLC.
49700  *
49701  * Originally Released Under LGPL - original licence link has changed is not relivant.
49702  *
49703  * Fork - LGPL
49704  * <script type="text/javascript">
49705  */
49706
49707 /**
49708  * @class Roo.grid.AbstractSelectionModel
49709  * @extends Roo.util.Observable
49710  * Abstract base class for grid SelectionModels.  It provides the interface that should be
49711  * implemented by descendant classes.  This class should not be directly instantiated.
49712  * @constructor
49713  */
49714 Roo.grid.AbstractSelectionModel = function(){
49715     this.locked = false;
49716     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
49717 };
49718
49719 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
49720     /** @ignore Called by the grid automatically. Do not call directly. */
49721     init : function(grid){
49722         this.grid = grid;
49723         this.initEvents();
49724     },
49725
49726     /**
49727      * Locks the selections.
49728      */
49729     lock : function(){
49730         this.locked = true;
49731     },
49732
49733     /**
49734      * Unlocks the selections.
49735      */
49736     unlock : function(){
49737         this.locked = false;
49738     },
49739
49740     /**
49741      * Returns true if the selections are locked.
49742      * @return {Boolean}
49743      */
49744     isLocked : function(){
49745         return this.locked;
49746     }
49747 });/*
49748  * Based on:
49749  * Ext JS Library 1.1.1
49750  * Copyright(c) 2006-2007, Ext JS, LLC.
49751  *
49752  * Originally Released Under LGPL - original licence link has changed is not relivant.
49753  *
49754  * Fork - LGPL
49755  * <script type="text/javascript">
49756  */
49757 /**
49758  * @extends Roo.grid.AbstractSelectionModel
49759  * @class Roo.grid.RowSelectionModel
49760  * The default SelectionModel used by {@link Roo.grid.Grid}.
49761  * It supports multiple selections and keyboard selection/navigation. 
49762  * @constructor
49763  * @param {Object} config
49764  */
49765 Roo.grid.RowSelectionModel = function(config){
49766     Roo.apply(this, config);
49767     this.selections = new Roo.util.MixedCollection(false, function(o){
49768         return o.id;
49769     });
49770
49771     this.last = false;
49772     this.lastActive = false;
49773
49774     this.addEvents({
49775         /**
49776              * @event selectionchange
49777              * Fires when the selection changes
49778              * @param {SelectionModel} this
49779              */
49780             "selectionchange" : true,
49781         /**
49782              * @event afterselectionchange
49783              * Fires after the selection changes (eg. by key press or clicking)
49784              * @param {SelectionModel} this
49785              */
49786             "afterselectionchange" : true,
49787         /**
49788              * @event beforerowselect
49789              * Fires when a row is selected being selected, return false to cancel.
49790              * @param {SelectionModel} this
49791              * @param {Number} rowIndex The selected index
49792              * @param {Boolean} keepExisting False if other selections will be cleared
49793              */
49794             "beforerowselect" : true,
49795         /**
49796              * @event rowselect
49797              * Fires when a row is selected.
49798              * @param {SelectionModel} this
49799              * @param {Number} rowIndex The selected index
49800              * @param {Roo.data.Record} r The record
49801              */
49802             "rowselect" : true,
49803         /**
49804              * @event rowdeselect
49805              * Fires when a row is deselected.
49806              * @param {SelectionModel} this
49807              * @param {Number} rowIndex The selected index
49808              */
49809         "rowdeselect" : true
49810     });
49811     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
49812     this.locked = false;
49813 };
49814
49815 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
49816     /**
49817      * @cfg {Boolean} singleSelect
49818      * True to allow selection of only one row at a time (defaults to false)
49819      */
49820     singleSelect : false,
49821
49822     // private
49823     initEvents : function(){
49824
49825         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
49826             this.grid.on("mousedown", this.handleMouseDown, this);
49827         }else{ // allow click to work like normal
49828             this.grid.on("rowclick", this.handleDragableRowClick, this);
49829         }
49830
49831         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
49832             "up" : function(e){
49833                 if(!e.shiftKey){
49834                     this.selectPrevious(e.shiftKey);
49835                 }else if(this.last !== false && this.lastActive !== false){
49836                     var last = this.last;
49837                     this.selectRange(this.last,  this.lastActive-1);
49838                     this.grid.getView().focusRow(this.lastActive);
49839                     if(last !== false){
49840                         this.last = last;
49841                     }
49842                 }else{
49843                     this.selectFirstRow();
49844                 }
49845                 this.fireEvent("afterselectionchange", this);
49846             },
49847             "down" : function(e){
49848                 if(!e.shiftKey){
49849                     this.selectNext(e.shiftKey);
49850                 }else if(this.last !== false && this.lastActive !== false){
49851                     var last = this.last;
49852                     this.selectRange(this.last,  this.lastActive+1);
49853                     this.grid.getView().focusRow(this.lastActive);
49854                     if(last !== false){
49855                         this.last = last;
49856                     }
49857                 }else{
49858                     this.selectFirstRow();
49859                 }
49860                 this.fireEvent("afterselectionchange", this);
49861             },
49862             scope: this
49863         });
49864
49865         var view = this.grid.view;
49866         view.on("refresh", this.onRefresh, this);
49867         view.on("rowupdated", this.onRowUpdated, this);
49868         view.on("rowremoved", this.onRemove, this);
49869     },
49870
49871     // private
49872     onRefresh : function(){
49873         var ds = this.grid.dataSource, i, v = this.grid.view;
49874         var s = this.selections;
49875         s.each(function(r){
49876             if((i = ds.indexOfId(r.id)) != -1){
49877                 v.onRowSelect(i);
49878             }else{
49879                 s.remove(r);
49880             }
49881         });
49882     },
49883
49884     // private
49885     onRemove : function(v, index, r){
49886         this.selections.remove(r);
49887     },
49888
49889     // private
49890     onRowUpdated : function(v, index, r){
49891         if(this.isSelected(r)){
49892             v.onRowSelect(index);
49893         }
49894     },
49895
49896     /**
49897      * Select records.
49898      * @param {Array} records The records to select
49899      * @param {Boolean} keepExisting (optional) True to keep existing selections
49900      */
49901     selectRecords : function(records, keepExisting){
49902         if(!keepExisting){
49903             this.clearSelections();
49904         }
49905         var ds = this.grid.dataSource;
49906         for(var i = 0, len = records.length; i < len; i++){
49907             this.selectRow(ds.indexOf(records[i]), true);
49908         }
49909     },
49910
49911     /**
49912      * Gets the number of selected rows.
49913      * @return {Number}
49914      */
49915     getCount : function(){
49916         return this.selections.length;
49917     },
49918
49919     /**
49920      * Selects the first row in the grid.
49921      */
49922     selectFirstRow : function(){
49923         this.selectRow(0);
49924     },
49925
49926     /**
49927      * Select the last row.
49928      * @param {Boolean} keepExisting (optional) True to keep existing selections
49929      */
49930     selectLastRow : function(keepExisting){
49931         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
49932     },
49933
49934     /**
49935      * Selects the row immediately following the last selected row.
49936      * @param {Boolean} keepExisting (optional) True to keep existing selections
49937      */
49938     selectNext : function(keepExisting){
49939         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
49940             this.selectRow(this.last+1, keepExisting);
49941             this.grid.getView().focusRow(this.last);
49942         }
49943     },
49944
49945     /**
49946      * Selects the row that precedes the last selected row.
49947      * @param {Boolean} keepExisting (optional) True to keep existing selections
49948      */
49949     selectPrevious : function(keepExisting){
49950         if(this.last){
49951             this.selectRow(this.last-1, keepExisting);
49952             this.grid.getView().focusRow(this.last);
49953         }
49954     },
49955
49956     /**
49957      * Returns the selected records
49958      * @return {Array} Array of selected records
49959      */
49960     getSelections : function(){
49961         return [].concat(this.selections.items);
49962     },
49963
49964     /**
49965      * Returns the first selected record.
49966      * @return {Record}
49967      */
49968     getSelected : function(){
49969         return this.selections.itemAt(0);
49970     },
49971
49972
49973     /**
49974      * Clears all selections.
49975      */
49976     clearSelections : function(fast){
49977         if(this.locked) return;
49978         if(fast !== true){
49979             var ds = this.grid.dataSource;
49980             var s = this.selections;
49981             s.each(function(r){
49982                 this.deselectRow(ds.indexOfId(r.id));
49983             }, this);
49984             s.clear();
49985         }else{
49986             this.selections.clear();
49987         }
49988         this.last = false;
49989     },
49990
49991
49992     /**
49993      * Selects all rows.
49994      */
49995     selectAll : function(){
49996         if(this.locked) return;
49997         this.selections.clear();
49998         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
49999             this.selectRow(i, true);
50000         }
50001     },
50002
50003     /**
50004      * Returns True if there is a selection.
50005      * @return {Boolean}
50006      */
50007     hasSelection : function(){
50008         return this.selections.length > 0;
50009     },
50010
50011     /**
50012      * Returns True if the specified row is selected.
50013      * @param {Number/Record} record The record or index of the record to check
50014      * @return {Boolean}
50015      */
50016     isSelected : function(index){
50017         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
50018         return (r && this.selections.key(r.id) ? true : false);
50019     },
50020
50021     /**
50022      * Returns True if the specified record id is selected.
50023      * @param {String} id The id of record to check
50024      * @return {Boolean}
50025      */
50026     isIdSelected : function(id){
50027         return (this.selections.key(id) ? true : false);
50028     },
50029
50030     // private
50031     handleMouseDown : function(e, t){
50032         var view = this.grid.getView(), rowIndex;
50033         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
50034             return;
50035         };
50036         if(e.shiftKey && this.last !== false){
50037             var last = this.last;
50038             this.selectRange(last, rowIndex, e.ctrlKey);
50039             this.last = last; // reset the last
50040             view.focusRow(rowIndex);
50041         }else{
50042             var isSelected = this.isSelected(rowIndex);
50043             if(e.button !== 0 && isSelected){
50044                 view.focusRow(rowIndex);
50045             }else if(e.ctrlKey && isSelected){
50046                 this.deselectRow(rowIndex);
50047             }else if(!isSelected){
50048                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
50049                 view.focusRow(rowIndex);
50050             }
50051         }
50052         this.fireEvent("afterselectionchange", this);
50053     },
50054     // private
50055     handleDragableRowClick :  function(grid, rowIndex, e) 
50056     {
50057         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
50058             this.selectRow(rowIndex, false);
50059             grid.view.focusRow(rowIndex);
50060              this.fireEvent("afterselectionchange", this);
50061         }
50062     },
50063     
50064     /**
50065      * Selects multiple rows.
50066      * @param {Array} rows Array of the indexes of the row to select
50067      * @param {Boolean} keepExisting (optional) True to keep existing selections
50068      */
50069     selectRows : function(rows, keepExisting){
50070         if(!keepExisting){
50071             this.clearSelections();
50072         }
50073         for(var i = 0, len = rows.length; i < len; i++){
50074             this.selectRow(rows[i], true);
50075         }
50076     },
50077
50078     /**
50079      * Selects a range of rows. All rows in between startRow and endRow are also selected.
50080      * @param {Number} startRow The index of the first row in the range
50081      * @param {Number} endRow The index of the last row in the range
50082      * @param {Boolean} keepExisting (optional) True to retain existing selections
50083      */
50084     selectRange : function(startRow, endRow, keepExisting){
50085         if(this.locked) return;
50086         if(!keepExisting){
50087             this.clearSelections();
50088         }
50089         if(startRow <= endRow){
50090             for(var i = startRow; i <= endRow; i++){
50091                 this.selectRow(i, true);
50092             }
50093         }else{
50094             for(var i = startRow; i >= endRow; i--){
50095                 this.selectRow(i, true);
50096             }
50097         }
50098     },
50099
50100     /**
50101      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
50102      * @param {Number} startRow The index of the first row in the range
50103      * @param {Number} endRow The index of the last row in the range
50104      */
50105     deselectRange : function(startRow, endRow, preventViewNotify){
50106         if(this.locked) return;
50107         for(var i = startRow; i <= endRow; i++){
50108             this.deselectRow(i, preventViewNotify);
50109         }
50110     },
50111
50112     /**
50113      * Selects a row.
50114      * @param {Number} row The index of the row to select
50115      * @param {Boolean} keepExisting (optional) True to keep existing selections
50116      */
50117     selectRow : function(index, keepExisting, preventViewNotify){
50118         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
50119         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
50120             if(!keepExisting || this.singleSelect){
50121                 this.clearSelections();
50122             }
50123             var r = this.grid.dataSource.getAt(index);
50124             this.selections.add(r);
50125             this.last = this.lastActive = index;
50126             if(!preventViewNotify){
50127                 this.grid.getView().onRowSelect(index);
50128             }
50129             this.fireEvent("rowselect", this, index, r);
50130             this.fireEvent("selectionchange", this);
50131         }
50132     },
50133
50134     /**
50135      * Deselects a row.
50136      * @param {Number} row The index of the row to deselect
50137      */
50138     deselectRow : function(index, preventViewNotify){
50139         if(this.locked) return;
50140         if(this.last == index){
50141             this.last = false;
50142         }
50143         if(this.lastActive == index){
50144             this.lastActive = false;
50145         }
50146         var r = this.grid.dataSource.getAt(index);
50147         this.selections.remove(r);
50148         if(!preventViewNotify){
50149             this.grid.getView().onRowDeselect(index);
50150         }
50151         this.fireEvent("rowdeselect", this, index);
50152         this.fireEvent("selectionchange", this);
50153     },
50154
50155     // private
50156     restoreLast : function(){
50157         if(this._last){
50158             this.last = this._last;
50159         }
50160     },
50161
50162     // private
50163     acceptsNav : function(row, col, cm){
50164         return !cm.isHidden(col) && cm.isCellEditable(col, row);
50165     },
50166
50167     // private
50168     onEditorKey : function(field, e){
50169         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
50170         if(k == e.TAB){
50171             e.stopEvent();
50172             ed.completeEdit();
50173             if(e.shiftKey){
50174                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
50175             }else{
50176                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50177             }
50178         }else if(k == e.ENTER && !e.ctrlKey){
50179             e.stopEvent();
50180             ed.completeEdit();
50181             if(e.shiftKey){
50182                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
50183             }else{
50184                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
50185             }
50186         }else if(k == e.ESC){
50187             ed.cancelEdit();
50188         }
50189         if(newCell){
50190             g.startEditing(newCell[0], newCell[1]);
50191         }
50192     }
50193 });/*
50194  * Based on:
50195  * Ext JS Library 1.1.1
50196  * Copyright(c) 2006-2007, Ext JS, LLC.
50197  *
50198  * Originally Released Under LGPL - original licence link has changed is not relivant.
50199  *
50200  * Fork - LGPL
50201  * <script type="text/javascript">
50202  */
50203 /**
50204  * @class Roo.grid.CellSelectionModel
50205  * @extends Roo.grid.AbstractSelectionModel
50206  * This class provides the basic implementation for cell selection in a grid.
50207  * @constructor
50208  * @param {Object} config The object containing the configuration of this model.
50209  */
50210 Roo.grid.CellSelectionModel = function(config){
50211     Roo.apply(this, config);
50212
50213     this.selection = null;
50214
50215     this.addEvents({
50216         /**
50217              * @event beforerowselect
50218              * Fires before a cell is selected.
50219              * @param {SelectionModel} this
50220              * @param {Number} rowIndex The selected row index
50221              * @param {Number} colIndex The selected cell index
50222              */
50223             "beforecellselect" : true,
50224         /**
50225              * @event cellselect
50226              * Fires when a cell is selected.
50227              * @param {SelectionModel} this
50228              * @param {Number} rowIndex The selected row index
50229              * @param {Number} colIndex The selected cell index
50230              */
50231             "cellselect" : true,
50232         /**
50233              * @event selectionchange
50234              * Fires when the active selection changes.
50235              * @param {SelectionModel} this
50236              * @param {Object} selection null for no selection or an object (o) with two properties
50237                 <ul>
50238                 <li>o.record: the record object for the row the selection is in</li>
50239                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
50240                 </ul>
50241              */
50242             "selectionchange" : true
50243     });
50244     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
50245 };
50246
50247 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
50248
50249     /** @ignore */
50250     initEvents : function(){
50251         this.grid.on("mousedown", this.handleMouseDown, this);
50252         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
50253         var view = this.grid.view;
50254         view.on("refresh", this.onViewChange, this);
50255         view.on("rowupdated", this.onRowUpdated, this);
50256         view.on("beforerowremoved", this.clearSelections, this);
50257         view.on("beforerowsinserted", this.clearSelections, this);
50258         if(this.grid.isEditor){
50259             this.grid.on("beforeedit", this.beforeEdit,  this);
50260         }
50261     },
50262
50263         //private
50264     beforeEdit : function(e){
50265         this.select(e.row, e.column, false, true, e.record);
50266     },
50267
50268         //private
50269     onRowUpdated : function(v, index, r){
50270         if(this.selection && this.selection.record == r){
50271             v.onCellSelect(index, this.selection.cell[1]);
50272         }
50273     },
50274
50275         //private
50276     onViewChange : function(){
50277         this.clearSelections(true);
50278     },
50279
50280         /**
50281          * Returns the currently selected cell,.
50282          * @return {Array} The selected cell (row, column) or null if none selected.
50283          */
50284     getSelectedCell : function(){
50285         return this.selection ? this.selection.cell : null;
50286     },
50287
50288     /**
50289      * Clears all selections.
50290      * @param {Boolean} true to prevent the gridview from being notified about the change.
50291      */
50292     clearSelections : function(preventNotify){
50293         var s = this.selection;
50294         if(s){
50295             if(preventNotify !== true){
50296                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
50297             }
50298             this.selection = null;
50299             this.fireEvent("selectionchange", this, null);
50300         }
50301     },
50302
50303     /**
50304      * Returns true if there is a selection.
50305      * @return {Boolean}
50306      */
50307     hasSelection : function(){
50308         return this.selection ? true : false;
50309     },
50310
50311     /** @ignore */
50312     handleMouseDown : function(e, t){
50313         var v = this.grid.getView();
50314         if(this.isLocked()){
50315             return;
50316         };
50317         var row = v.findRowIndex(t);
50318         var cell = v.findCellIndex(t);
50319         if(row !== false && cell !== false){
50320             this.select(row, cell);
50321         }
50322     },
50323
50324     /**
50325      * Selects a cell.
50326      * @param {Number} rowIndex
50327      * @param {Number} collIndex
50328      */
50329     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
50330         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
50331             this.clearSelections();
50332             r = r || this.grid.dataSource.getAt(rowIndex);
50333             this.selection = {
50334                 record : r,
50335                 cell : [rowIndex, colIndex]
50336             };
50337             if(!preventViewNotify){
50338                 var v = this.grid.getView();
50339                 v.onCellSelect(rowIndex, colIndex);
50340                 if(preventFocus !== true){
50341                     v.focusCell(rowIndex, colIndex);
50342                 }
50343             }
50344             this.fireEvent("cellselect", this, rowIndex, colIndex);
50345             this.fireEvent("selectionchange", this, this.selection);
50346         }
50347     },
50348
50349         //private
50350     isSelectable : function(rowIndex, colIndex, cm){
50351         return !cm.isHidden(colIndex);
50352     },
50353
50354     /** @ignore */
50355     handleKeyDown : function(e){
50356         Roo.log('Cell Sel Model handleKeyDown');
50357         if(!e.isNavKeyPress()){
50358             return;
50359         }
50360         var g = this.grid, s = this.selection;
50361         if(!s){
50362             e.stopEvent();
50363             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
50364             if(cell){
50365                 this.select(cell[0], cell[1]);
50366             }
50367             return;
50368         }
50369         var sm = this;
50370         var walk = function(row, col, step){
50371             return g.walkCells(row, col, step, sm.isSelectable,  sm);
50372         };
50373         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
50374         var newCell;
50375
50376         switch(k){
50377             case e.TAB:
50378                 // handled by onEditorKey
50379                 if (g.isEditor && g.editing) {
50380                     return;
50381                 }
50382                 if(e.shiftKey){
50383                      newCell = walk(r, c-1, -1);
50384                 }else{
50385                      newCell = walk(r, c+1, 1);
50386                 }
50387              break;
50388              case e.DOWN:
50389                  newCell = walk(r+1, c, 1);
50390              break;
50391              case e.UP:
50392                  newCell = walk(r-1, c, -1);
50393              break;
50394              case e.RIGHT:
50395                  newCell = walk(r, c+1, 1);
50396              break;
50397              case e.LEFT:
50398                  newCell = walk(r, c-1, -1);
50399              break;
50400              case e.ENTER:
50401                  if(g.isEditor && !g.editing){
50402                     g.startEditing(r, c);
50403                     e.stopEvent();
50404                     return;
50405                 }
50406              break;
50407         };
50408         if(newCell){
50409             this.select(newCell[0], newCell[1]);
50410             e.stopEvent();
50411         }
50412     },
50413
50414     acceptsNav : function(row, col, cm){
50415         return !cm.isHidden(col) && cm.isCellEditable(col, row);
50416     },
50417
50418     onEditorKey : function(field, e){
50419         
50420         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
50421         ///Roo.log('onEditorKey' + k);
50422         
50423         if(k == e.TAB){
50424             if(e.shiftKey){
50425                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
50426             }else{
50427                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50428             }
50429             e.stopEvent();
50430         }else if(k == e.ENTER && !e.ctrlKey){
50431             ed.completeEdit();
50432             e.stopEvent();
50433             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50434         }else if(k == e.ESC){
50435             ed.cancelEdit();
50436         }
50437         
50438         
50439         if(newCell){
50440             //Roo.log('next cell after edit');
50441             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
50442         }
50443     }
50444 });/*
50445  * Based on:
50446  * Ext JS Library 1.1.1
50447  * Copyright(c) 2006-2007, Ext JS, LLC.
50448  *
50449  * Originally Released Under LGPL - original licence link has changed is not relivant.
50450  *
50451  * Fork - LGPL
50452  * <script type="text/javascript">
50453  */
50454  
50455 /**
50456  * @class Roo.grid.EditorGrid
50457  * @extends Roo.grid.Grid
50458  * Class for creating and editable grid.
50459  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
50460  * The container MUST have some type of size defined for the grid to fill. The container will be 
50461  * automatically set to position relative if it isn't already.
50462  * @param {Object} dataSource The data model to bind to
50463  * @param {Object} colModel The column model with info about this grid's columns
50464  */
50465 Roo.grid.EditorGrid = function(container, config){
50466     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
50467     this.getGridEl().addClass("xedit-grid");
50468
50469     if(!this.selModel){
50470         this.selModel = new Roo.grid.CellSelectionModel();
50471     }
50472
50473     this.activeEditor = null;
50474
50475         this.addEvents({
50476             /**
50477              * @event beforeedit
50478              * Fires before cell editing is triggered. The edit event object has the following properties <br />
50479              * <ul style="padding:5px;padding-left:16px;">
50480              * <li>grid - This grid</li>
50481              * <li>record - The record being edited</li>
50482              * <li>field - The field name being edited</li>
50483              * <li>value - The value for the field being edited.</li>
50484              * <li>row - The grid row index</li>
50485              * <li>column - The grid column index</li>
50486              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50487              * </ul>
50488              * @param {Object} e An edit event (see above for description)
50489              */
50490             "beforeedit" : true,
50491             /**
50492              * @event afteredit
50493              * Fires after a cell is edited. <br />
50494              * <ul style="padding:5px;padding-left:16px;">
50495              * <li>grid - This grid</li>
50496              * <li>record - The record being edited</li>
50497              * <li>field - The field name being edited</li>
50498              * <li>value - The value being set</li>
50499              * <li>originalValue - The original value for the field, before the edit.</li>
50500              * <li>row - The grid row index</li>
50501              * <li>column - The grid column index</li>
50502              * </ul>
50503              * @param {Object} e An edit event (see above for description)
50504              */
50505             "afteredit" : true,
50506             /**
50507              * @event validateedit
50508              * Fires after a cell is edited, but before the value is set in the record. 
50509          * You can use this to modify the value being set in the field, Return false
50510              * to cancel the change. The edit event object has the following properties <br />
50511              * <ul style="padding:5px;padding-left:16px;">
50512          * <li>editor - This editor</li>
50513              * <li>grid - This grid</li>
50514              * <li>record - The record being edited</li>
50515              * <li>field - The field name being edited</li>
50516              * <li>value - The value being set</li>
50517              * <li>originalValue - The original value for the field, before the edit.</li>
50518              * <li>row - The grid row index</li>
50519              * <li>column - The grid column index</li>
50520              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50521              * </ul>
50522              * @param {Object} e An edit event (see above for description)
50523              */
50524             "validateedit" : true
50525         });
50526     this.on("bodyscroll", this.stopEditing,  this);
50527     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
50528 };
50529
50530 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
50531     /**
50532      * @cfg {Number} clicksToEdit
50533      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
50534      */
50535     clicksToEdit: 2,
50536
50537     // private
50538     isEditor : true,
50539     // private
50540     trackMouseOver: false, // causes very odd FF errors
50541
50542     onCellDblClick : function(g, row, col){
50543         this.startEditing(row, col);
50544     },
50545
50546     onEditComplete : function(ed, value, startValue){
50547         this.editing = false;
50548         this.activeEditor = null;
50549         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
50550         var r = ed.record;
50551         var field = this.colModel.getDataIndex(ed.col);
50552         var e = {
50553             grid: this,
50554             record: r,
50555             field: field,
50556             originalValue: startValue,
50557             value: value,
50558             row: ed.row,
50559             column: ed.col,
50560             cancel:false,
50561             editor: ed
50562         };
50563         if(String(value) !== String(startValue)){
50564             
50565             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
50566                 r.set(field, e.value);
50567                 // if we are dealing with a combo box..
50568                 // then we also set the 'name' colum to be the displayField
50569                 if (ed.field.displayField && ed.field.name) {
50570                     r.set(ed.field.name, ed.field.el.dom.value);
50571                 }
50572                 
50573                 delete e.cancel; //?? why!!!
50574                 this.fireEvent("afteredit", e);
50575             }
50576         } else {
50577             this.fireEvent("afteredit", e); // always fire it!
50578         }
50579         this.view.focusCell(ed.row, ed.col);
50580     },
50581
50582     /**
50583      * Starts editing the specified for the specified row/column
50584      * @param {Number} rowIndex
50585      * @param {Number} colIndex
50586      */
50587     startEditing : function(row, col){
50588         this.stopEditing();
50589         if(this.colModel.isCellEditable(col, row)){
50590             this.view.ensureVisible(row, col, true);
50591             var r = this.dataSource.getAt(row);
50592             var field = this.colModel.getDataIndex(col);
50593             var e = {
50594                 grid: this,
50595                 record: r,
50596                 field: field,
50597                 value: r.data[field],
50598                 row: row,
50599                 column: col,
50600                 cancel:false
50601             };
50602             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
50603                 this.editing = true;
50604                 var ed = this.colModel.getCellEditor(col, row);
50605                 
50606                 if (!ed) {
50607                     return;
50608                 }
50609                 if(!ed.rendered){
50610                     ed.render(ed.parentEl || document.body);
50611                 }
50612                 ed.field.reset();
50613                 (function(){ // complex but required for focus issues in safari, ie and opera
50614                     ed.row = row;
50615                     ed.col = col;
50616                     ed.record = r;
50617                     ed.on("complete", this.onEditComplete, this, {single: true});
50618                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
50619                     this.activeEditor = ed;
50620                     var v = r.data[field];
50621                     ed.startEdit(this.view.getCell(row, col), v);
50622                     // combo's with 'displayField and name set
50623                     if (ed.field.displayField && ed.field.name) {
50624                         ed.field.el.dom.value = r.data[ed.field.name];
50625                     }
50626                     
50627                     
50628                 }).defer(50, this);
50629             }
50630         }
50631     },
50632         
50633     /**
50634      * Stops any active editing
50635      */
50636     stopEditing : function(){
50637         if(this.activeEditor){
50638             this.activeEditor.completeEdit();
50639         }
50640         this.activeEditor = null;
50641     }
50642 });/*
50643  * Based on:
50644  * Ext JS Library 1.1.1
50645  * Copyright(c) 2006-2007, Ext JS, LLC.
50646  *
50647  * Originally Released Under LGPL - original licence link has changed is not relivant.
50648  *
50649  * Fork - LGPL
50650  * <script type="text/javascript">
50651  */
50652
50653 // private - not really -- you end up using it !
50654 // This is a support class used internally by the Grid components
50655
50656 /**
50657  * @class Roo.grid.GridEditor
50658  * @extends Roo.Editor
50659  * Class for creating and editable grid elements.
50660  * @param {Object} config any settings (must include field)
50661  */
50662 Roo.grid.GridEditor = function(field, config){
50663     if (!config && field.field) {
50664         config = field;
50665         field = Roo.factory(config.field, Roo.form);
50666     }
50667     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
50668     field.monitorTab = false;
50669 };
50670
50671 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
50672     
50673     /**
50674      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
50675      */
50676     
50677     alignment: "tl-tl",
50678     autoSize: "width",
50679     hideEl : false,
50680     cls: "x-small-editor x-grid-editor",
50681     shim:false,
50682     shadow:"frame"
50683 });/*
50684  * Based on:
50685  * Ext JS Library 1.1.1
50686  * Copyright(c) 2006-2007, Ext JS, LLC.
50687  *
50688  * Originally Released Under LGPL - original licence link has changed is not relivant.
50689  *
50690  * Fork - LGPL
50691  * <script type="text/javascript">
50692  */
50693   
50694
50695   
50696 Roo.grid.PropertyRecord = Roo.data.Record.create([
50697     {name:'name',type:'string'},  'value'
50698 ]);
50699
50700
50701 Roo.grid.PropertyStore = function(grid, source){
50702     this.grid = grid;
50703     this.store = new Roo.data.Store({
50704         recordType : Roo.grid.PropertyRecord
50705     });
50706     this.store.on('update', this.onUpdate,  this);
50707     if(source){
50708         this.setSource(source);
50709     }
50710     Roo.grid.PropertyStore.superclass.constructor.call(this);
50711 };
50712
50713
50714
50715 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
50716     setSource : function(o){
50717         this.source = o;
50718         this.store.removeAll();
50719         var data = [];
50720         for(var k in o){
50721             if(this.isEditableValue(o[k])){
50722                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
50723             }
50724         }
50725         this.store.loadRecords({records: data}, {}, true);
50726     },
50727
50728     onUpdate : function(ds, record, type){
50729         if(type == Roo.data.Record.EDIT){
50730             var v = record.data['value'];
50731             var oldValue = record.modified['value'];
50732             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
50733                 this.source[record.id] = v;
50734                 record.commit();
50735                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
50736             }else{
50737                 record.reject();
50738             }
50739         }
50740     },
50741
50742     getProperty : function(row){
50743        return this.store.getAt(row);
50744     },
50745
50746     isEditableValue: function(val){
50747         if(val && val instanceof Date){
50748             return true;
50749         }else if(typeof val == 'object' || typeof val == 'function'){
50750             return false;
50751         }
50752         return true;
50753     },
50754
50755     setValue : function(prop, value){
50756         this.source[prop] = value;
50757         this.store.getById(prop).set('value', value);
50758     },
50759
50760     getSource : function(){
50761         return this.source;
50762     }
50763 });
50764
50765 Roo.grid.PropertyColumnModel = function(grid, store){
50766     this.grid = grid;
50767     var g = Roo.grid;
50768     g.PropertyColumnModel.superclass.constructor.call(this, [
50769         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
50770         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
50771     ]);
50772     this.store = store;
50773     this.bselect = Roo.DomHelper.append(document.body, {
50774         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
50775             {tag: 'option', value: 'true', html: 'true'},
50776             {tag: 'option', value: 'false', html: 'false'}
50777         ]
50778     });
50779     Roo.id(this.bselect);
50780     var f = Roo.form;
50781     this.editors = {
50782         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
50783         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
50784         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
50785         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
50786         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
50787     };
50788     this.renderCellDelegate = this.renderCell.createDelegate(this);
50789     this.renderPropDelegate = this.renderProp.createDelegate(this);
50790 };
50791
50792 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
50793     
50794     
50795     nameText : 'Name',
50796     valueText : 'Value',
50797     
50798     dateFormat : 'm/j/Y',
50799     
50800     
50801     renderDate : function(dateVal){
50802         return dateVal.dateFormat(this.dateFormat);
50803     },
50804
50805     renderBool : function(bVal){
50806         return bVal ? 'true' : 'false';
50807     },
50808
50809     isCellEditable : function(colIndex, rowIndex){
50810         return colIndex == 1;
50811     },
50812
50813     getRenderer : function(col){
50814         return col == 1 ?
50815             this.renderCellDelegate : this.renderPropDelegate;
50816     },
50817
50818     renderProp : function(v){
50819         return this.getPropertyName(v);
50820     },
50821
50822     renderCell : function(val){
50823         var rv = val;
50824         if(val instanceof Date){
50825             rv = this.renderDate(val);
50826         }else if(typeof val == 'boolean'){
50827             rv = this.renderBool(val);
50828         }
50829         return Roo.util.Format.htmlEncode(rv);
50830     },
50831
50832     getPropertyName : function(name){
50833         var pn = this.grid.propertyNames;
50834         return pn && pn[name] ? pn[name] : name;
50835     },
50836
50837     getCellEditor : function(colIndex, rowIndex){
50838         var p = this.store.getProperty(rowIndex);
50839         var n = p.data['name'], val = p.data['value'];
50840         
50841         if(typeof(this.grid.customEditors[n]) == 'string'){
50842             return this.editors[this.grid.customEditors[n]];
50843         }
50844         if(typeof(this.grid.customEditors[n]) != 'undefined'){
50845             return this.grid.customEditors[n];
50846         }
50847         if(val instanceof Date){
50848             return this.editors['date'];
50849         }else if(typeof val == 'number'){
50850             return this.editors['number'];
50851         }else if(typeof val == 'boolean'){
50852             return this.editors['boolean'];
50853         }else{
50854             return this.editors['string'];
50855         }
50856     }
50857 });
50858
50859 /**
50860  * @class Roo.grid.PropertyGrid
50861  * @extends Roo.grid.EditorGrid
50862  * This class represents the  interface of a component based property grid control.
50863  * <br><br>Usage:<pre><code>
50864  var grid = new Roo.grid.PropertyGrid("my-container-id", {
50865       
50866  });
50867  // set any options
50868  grid.render();
50869  * </code></pre>
50870   
50871  * @constructor
50872  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
50873  * The container MUST have some type of size defined for the grid to fill. The container will be
50874  * automatically set to position relative if it isn't already.
50875  * @param {Object} config A config object that sets properties on this grid.
50876  */
50877 Roo.grid.PropertyGrid = function(container, config){
50878     config = config || {};
50879     var store = new Roo.grid.PropertyStore(this);
50880     this.store = store;
50881     var cm = new Roo.grid.PropertyColumnModel(this, store);
50882     store.store.sort('name', 'ASC');
50883     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
50884         ds: store.store,
50885         cm: cm,
50886         enableColLock:false,
50887         enableColumnMove:false,
50888         stripeRows:false,
50889         trackMouseOver: false,
50890         clicksToEdit:1
50891     }, config));
50892     this.getGridEl().addClass('x-props-grid');
50893     this.lastEditRow = null;
50894     this.on('columnresize', this.onColumnResize, this);
50895     this.addEvents({
50896          /**
50897              * @event beforepropertychange
50898              * Fires before a property changes (return false to stop?)
50899              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50900              * @param {String} id Record Id
50901              * @param {String} newval New Value
50902          * @param {String} oldval Old Value
50903              */
50904         "beforepropertychange": true,
50905         /**
50906              * @event propertychange
50907              * Fires after a property changes
50908              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50909              * @param {String} id Record Id
50910              * @param {String} newval New Value
50911          * @param {String} oldval Old Value
50912              */
50913         "propertychange": true
50914     });
50915     this.customEditors = this.customEditors || {};
50916 };
50917 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
50918     
50919      /**
50920      * @cfg {Object} customEditors map of colnames=> custom editors.
50921      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
50922      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
50923      * false disables editing of the field.
50924          */
50925     
50926       /**
50927      * @cfg {Object} propertyNames map of property Names to their displayed value
50928          */
50929     
50930     render : function(){
50931         Roo.grid.PropertyGrid.superclass.render.call(this);
50932         this.autoSize.defer(100, this);
50933     },
50934
50935     autoSize : function(){
50936         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
50937         if(this.view){
50938             this.view.fitColumns();
50939         }
50940     },
50941
50942     onColumnResize : function(){
50943         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
50944         this.autoSize();
50945     },
50946     /**
50947      * Sets the data for the Grid
50948      * accepts a Key => Value object of all the elements avaiable.
50949      * @param {Object} data  to appear in grid.
50950      */
50951     setSource : function(source){
50952         this.store.setSource(source);
50953         //this.autoSize();
50954     },
50955     /**
50956      * Gets all the data from the grid.
50957      * @return {Object} data  data stored in grid
50958      */
50959     getSource : function(){
50960         return this.store.getSource();
50961     }
50962 });/*
50963  * Based on:
50964  * Ext JS Library 1.1.1
50965  * Copyright(c) 2006-2007, Ext JS, LLC.
50966  *
50967  * Originally Released Under LGPL - original licence link has changed is not relivant.
50968  *
50969  * Fork - LGPL
50970  * <script type="text/javascript">
50971  */
50972  
50973 /**
50974  * @class Roo.LoadMask
50975  * A simple utility class for generically masking elements while loading data.  If the element being masked has
50976  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
50977  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
50978  * element's UpdateManager load indicator and will be destroyed after the initial load.
50979  * @constructor
50980  * Create a new LoadMask
50981  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
50982  * @param {Object} config The config object
50983  */
50984 Roo.LoadMask = function(el, config){
50985     this.el = Roo.get(el);
50986     Roo.apply(this, config);
50987     if(this.store){
50988         this.store.on('beforeload', this.onBeforeLoad, this);
50989         this.store.on('load', this.onLoad, this);
50990         this.store.on('loadexception', this.onLoad, this);
50991         this.removeMask = false;
50992     }else{
50993         var um = this.el.getUpdateManager();
50994         um.showLoadIndicator = false; // disable the default indicator
50995         um.on('beforeupdate', this.onBeforeLoad, this);
50996         um.on('update', this.onLoad, this);
50997         um.on('failure', this.onLoad, this);
50998         this.removeMask = true;
50999     }
51000 };
51001
51002 Roo.LoadMask.prototype = {
51003     /**
51004      * @cfg {Boolean} removeMask
51005      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
51006      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
51007      */
51008     /**
51009      * @cfg {String} msg
51010      * The text to display in a centered loading message box (defaults to 'Loading...')
51011      */
51012     msg : 'Loading...',
51013     /**
51014      * @cfg {String} msgCls
51015      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
51016      */
51017     msgCls : 'x-mask-loading',
51018
51019     /**
51020      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
51021      * @type Boolean
51022      */
51023     disabled: false,
51024
51025     /**
51026      * Disables the mask to prevent it from being displayed
51027      */
51028     disable : function(){
51029        this.disabled = true;
51030     },
51031
51032     /**
51033      * Enables the mask so that it can be displayed
51034      */
51035     enable : function(){
51036         this.disabled = false;
51037     },
51038
51039     // private
51040     onLoad : function(){
51041         this.el.unmask(this.removeMask);
51042     },
51043
51044     // private
51045     onBeforeLoad : function(){
51046         if(!this.disabled){
51047             this.el.mask(this.msg, this.msgCls);
51048         }
51049     },
51050
51051     // private
51052     destroy : function(){
51053         if(this.store){
51054             this.store.un('beforeload', this.onBeforeLoad, this);
51055             this.store.un('load', this.onLoad, this);
51056             this.store.un('loadexception', this.onLoad, this);
51057         }else{
51058             var um = this.el.getUpdateManager();
51059             um.un('beforeupdate', this.onBeforeLoad, this);
51060             um.un('update', this.onLoad, this);
51061             um.un('failure', this.onLoad, this);
51062         }
51063     }
51064 };/*
51065  * Based on:
51066  * Ext JS Library 1.1.1
51067  * Copyright(c) 2006-2007, Ext JS, LLC.
51068  *
51069  * Originally Released Under LGPL - original licence link has changed is not relivant.
51070  *
51071  * Fork - LGPL
51072  * <script type="text/javascript">
51073  */
51074 Roo.XTemplate = function(){
51075     Roo.XTemplate.superclass.constructor.apply(this, arguments);
51076     var s = this.html;
51077
51078     s = ['<tpl>', s, '</tpl>'].join('');
51079
51080     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
51081
51082     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
51083     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
51084     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
51085     var m, id = 0;
51086     var tpls = [];
51087
51088     while(m = s.match(re)){
51089        var m2 = m[0].match(nameRe);
51090        var m3 = m[0].match(ifRe);
51091        var m4 = m[0].match(execRe);
51092        var exp = null, fn = null, exec = null;
51093        var name = m2 && m2[1] ? m2[1] : '';
51094        if(m3){
51095            exp = m3 && m3[1] ? m3[1] : null;
51096            if(exp){
51097                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
51098            }
51099        }
51100        if(m4){
51101            exp = m4 && m4[1] ? m4[1] : null;
51102            if(exp){
51103                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
51104            }
51105        }
51106        if(name){
51107            switch(name){
51108                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
51109                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
51110                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
51111            }
51112        }
51113        tpls.push({
51114             id: id,
51115             target: name,
51116             exec: exec,
51117             test: fn,
51118             body: m[1]||''
51119         });
51120        s = s.replace(m[0], '{xtpl'+ id + '}');
51121        ++id;
51122     }
51123     for(var i = tpls.length-1; i >= 0; --i){
51124         this.compileTpl(tpls[i]);
51125     }
51126     this.master = tpls[tpls.length-1];
51127     this.tpls = tpls;
51128 };
51129 Roo.extend(Roo.XTemplate, Roo.Template, {
51130
51131     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
51132
51133     applySubTemplate : function(id, values, parent){
51134         var t = this.tpls[id];
51135         if(t.test && !t.test.call(this, values, parent)){
51136             return '';
51137         }
51138         if(t.exec && t.exec.call(this, values, parent)){
51139             return '';
51140         }
51141         var vs = t.target ? t.target.call(this, values, parent) : values;
51142         parent = t.target ? values : parent;
51143         if(t.target && vs instanceof Array){
51144             var buf = [];
51145             for(var i = 0, len = vs.length; i < len; i++){
51146                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
51147             }
51148             return buf.join('');
51149         }
51150         return t.compiled.call(this, vs, parent);
51151     },
51152
51153     compileTpl : function(tpl){
51154         var fm = Roo.util.Format;
51155         var useF = this.disableFormats !== true;
51156         var sep = Roo.isGecko ? "+" : ",";
51157         var fn = function(m, name, format, args){
51158             if(name.substr(0, 4) == 'xtpl'){
51159                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
51160             }
51161             var v;
51162             if(name.indexOf('.') != -1){
51163                 v = name;
51164             }else{
51165                 v = "values['" + name + "']";
51166             }
51167             if(format && useF){
51168                 args = args ? ',' + args : "";
51169                 if(format.substr(0, 5) != "this."){
51170                     format = "fm." + format + '(';
51171                 }else{
51172                     format = 'this.call("'+ format.substr(5) + '", ';
51173                     args = ", values";
51174                 }
51175             }else{
51176                 args= ''; format = "("+v+" === undefined ? '' : ";
51177             }
51178             return "'"+ sep + format + v + args + ")"+sep+"'";
51179         };
51180         var body;
51181         // branched to use + in gecko and [].join() in others
51182         if(Roo.isGecko){
51183             body = "tpl.compiled = function(values, parent){ return '" +
51184                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
51185                     "';};";
51186         }else{
51187             body = ["tpl.compiled = function(values, parent){ return ['"];
51188             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
51189             body.push("'].join('');};");
51190             body = body.join('');
51191         }
51192         /** eval:var:zzzzzzz */
51193         eval(body);
51194         return this;
51195     },
51196
51197     applyTemplate : function(values){
51198         return this.master.compiled.call(this, values, {});
51199         var s = this.subs;
51200     },
51201
51202     apply : function(){
51203         return this.applyTemplate.apply(this, arguments);
51204     },
51205
51206     compile : function(){return this;}
51207 });
51208
51209 Roo.XTemplate.from = function(el){
51210     el = Roo.getDom(el);
51211     return new Roo.XTemplate(el.value || el.innerHTML);
51212 };/*
51213  * Original code for Roojs - LGPL
51214  * <script type="text/javascript">
51215  */
51216  
51217 /**
51218  * @class Roo.XComponent
51219  * A delayed Element creator...
51220  * 
51221  * Mypart.xyx = new Roo.XComponent({
51222
51223     parent : 'Mypart.xyz', // empty == document.element.!!
51224     order : '001',
51225     name : 'xxxx'
51226     region : 'xxxx'
51227     disabled : function() {} 
51228      
51229     tree : function() { // return an tree of xtype declared components
51230         var MODULE = this;
51231         return 
51232         {
51233             xtype : 'NestedLayoutPanel',
51234             // technicall
51235         }
51236      ]
51237  *})
51238  *
51239  * Roo.onReady(function() {
51240     Roo.XComponent.build();
51241  })
51242  *
51243  * @extends Roo.util.Observable
51244  * @constructor
51245  * @param cfg {Object} configuration of component
51246  * 
51247  */
51248 Roo.XComponent = function(cfg) {
51249     Roo.apply(this, cfg);
51250     this.addEvents({ 
51251         /**
51252              * @event built
51253              * Fires when this the componnt is built
51254              * @param {Roo.XComponent} c the component
51255              */
51256         'built' : true,
51257         /**
51258              * @event buildcomplete
51259              * Fires on the top level element when all elements have been built
51260              * @param {Roo.XComponent} c the top level component.
51261          */
51262         'buildcomplete' : true
51263         
51264     });
51265     
51266     Roo.XComponent.register(this);
51267     this.modules = false;
51268     this.el = false; // where the layout goes..
51269     
51270     
51271 }
51272 Roo.extend(Roo.XComponent, Roo.util.Observable, {
51273     /**
51274      * @property el
51275      * The created element (with Roo.factory())
51276      * @type {Roo.Layout}
51277      */
51278     el  : false,
51279     
51280     /**
51281      * @property el
51282      * for BC  - use el in new code
51283      * @type {Roo.Layout}
51284      */
51285     panel : false,
51286     
51287     /**
51288      * @property layout
51289      * for BC  - use el in new code
51290      * @type {Roo.Layout}
51291      */
51292     layout : false,
51293     
51294      /**
51295      * @cfg {Function|boolean} disabled
51296      * If this module is disabled by some rule, return true from the funtion
51297      */
51298     disabled : false,
51299     
51300     /**
51301      * @cfg {String} parent 
51302      * Name of parent element which it get xtype added to..
51303      */
51304     parent: false,
51305     
51306     /**
51307      * @cfg {String} order
51308      * Used to set the order in which elements are created (usefull for multiple tabs)
51309      */
51310     
51311     order : false,
51312     /**
51313      * @cfg {String} name
51314      * String to display while loading.
51315      */
51316     name : false,
51317     /**
51318      * @cfg {Array} items
51319      * A single item array - the first element is the root of the tree..
51320      * It's done this way to stay compatible with the Xtype system...
51321      */
51322     items : false
51323      
51324 });
51325
51326 Roo.apply(Roo.XComponent, {
51327     
51328     /**
51329      * @property  buildCompleted
51330      * True when the builder has completed building the interface.
51331      * @type Boolean
51332      */
51333     buildCompleted : false,
51334      
51335     /**
51336      * @property  topModule
51337      * the upper most module - uses document.element as it's constructor.
51338      * @type Object
51339      */
51340      
51341     topModule  : false,
51342       
51343     /**
51344      * @property  modules
51345      * array of modules to be created by registration system.
51346      * @type {Array} of Roo.XComponent
51347      */
51348     
51349     modules : [],
51350       
51351      /**
51352      * @property  elmodules
51353      * array of modules to be created by which use #ID 
51354      * @type {Array} of Roo.XComponent
51355      */
51356     
51357     elmodules : [],
51358     /**
51359      * Register components to be built later.
51360      *
51361      * This solves the following issues
51362      * - Building is not done on page load, but after an authentication process has occured.
51363      * - Interface elements are registered on page load
51364      * - Parent Interface elements may not be loaded before child, so this handles that..
51365      * 
51366      *
51367      * example:
51368      * 
51369      * MyApp.register({
51370           order : '000001',
51371           module : 'Pman.Tab.projectMgr',
51372           region : 'center',
51373           parent : 'Pman.layout',
51374           disabled : false,  // or use a function..
51375         })
51376      
51377      * * @param {Object} details about module
51378      */
51379     register : function(obj) {
51380         this.modules.push(obj);
51381          
51382     },
51383     /**
51384      * convert a string to an object..
51385      * eg. 'AAA.BBB' -> finds AAA.BBB
51386      * 
51387      */
51388     
51389     toObject : function(str)
51390     {
51391         if (!str || typeof(str) == 'object') {
51392             return str;
51393         }
51394         if (str[0]=='#') {
51395             return str;
51396         }
51397         var ar = str.split('.');
51398         var rt, o;
51399         rt = ar.shift();
51400             /** eval:var:o */
51401         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
51402         if (o === false) {
51403              
51404             
51405             
51406             throw "Module not found : " + str;
51407         }
51408         Roo.each(ar, function(e) {
51409             if (typeof(o[e]) == 'undefined') {
51410                 throw "Module not found : " + str;
51411             }
51412             o = o[e];
51413         });
51414         return o;
51415         
51416     },
51417     
51418     
51419     /**
51420      * move modules into their correct place in the tree..
51421      * 
51422      */
51423     preBuild : function ()
51424     {
51425         
51426         Roo.each(this.modules , function (obj)
51427         {
51428             obj.parent = this.toObject(obj.parent);
51429             
51430             if (!obj.parent) {
51431                 this.topModule = obj;
51432                 return;
51433             }
51434             if (typeof(obj.parent) == 'string') {
51435                 this.elmodules.push(obj);
51436                 return;
51437             }
51438             
51439             if (!obj.parent.modules) {
51440                 obj.parent.modules = new Roo.util.MixedCollection(false, 
51441                     function(o) { return o.order + '' }
51442                 );
51443             }
51444             
51445             obj.parent.modules.add(obj);
51446         }, this);
51447     },
51448     
51449      /**
51450      * make a list of modules to build.
51451      * @return {Array} list of modules. 
51452      */ 
51453     
51454     buildOrder : function()
51455     {
51456         var _this = this;
51457         var cmp = function(a,b) {   
51458             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
51459         };
51460         
51461         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
51462             throw "No top level modules to build";
51463         }
51464         
51465         // make a flat list in order of modules to build.
51466         var mods = this.topModule ? [ this.topModule ] : [];
51467         Roo.each(this.elmodules,function(e) { mods.push(e) });
51468         
51469         
51470         // add modules to their parents..
51471         var addMod = function(m) {
51472            // Roo.debug && Roo.log(m.modKey);
51473             
51474             mods.push(m);
51475             if (m.modules) {
51476                 m.modules.keySort('ASC',  cmp );
51477                 m.modules.each(addMod);
51478             }
51479             // not sure if this is used any more..
51480             if (m.finalize) {
51481                 m.finalize.name = m.name + " (clean up) ";
51482                 mods.push(m.finalize);
51483             }
51484             
51485         }
51486         if (this.topModule) { 
51487             this.topModule.modules.keySort('ASC',  cmp );
51488             this.topModule.modules.each(addMod);
51489         }
51490         return mods;
51491     },
51492     
51493      /**
51494      * Build the registered modules.
51495      * @param {Object} parent element.
51496      * @param {Function} optional method to call after module has been added.
51497      * 
51498      */ 
51499    
51500     build : function() 
51501     {
51502         
51503         this.preBuild();
51504         var mods = this.buildOrder();
51505       
51506         //this.allmods = mods;
51507         //Roo.debug && Roo.log(mods);
51508         //return;
51509         if (!mods.length) { // should not happen
51510             throw "NO modules!!!";
51511         }
51512         
51513         
51514         
51515         // flash it up as modal - so we store the mask!?
51516         Roo.MessageBox.show({ title: 'loading' });
51517         Roo.MessageBox.show({
51518            title: "Please wait...",
51519            msg: "Building Interface...",
51520            width:450,
51521            progress:true,
51522            closable:false,
51523            modal: false
51524           
51525         });
51526         var total = mods.length;
51527         
51528         var _this = this;
51529         var progressRun = function() {
51530             if (!mods.length) {
51531                 Roo.debug && Roo.log('hide?');
51532                 Roo.MessageBox.hide();
51533                 if (_this.topModule) { 
51534                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
51535                 }
51536                 // THE END...
51537                 return false;    
51538             }
51539             
51540             var m = mods.shift();
51541             Roo.debug && Roo.log(m);
51542             if (typeof(m) == 'function') { // not sure if this is supported any more..
51543                 m.call(this);
51544                 return progressRun.defer(10, _this);
51545             } 
51546             
51547             Roo.MessageBox.updateProgress(
51548                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
51549                     " of " + total + 
51550                     (m.name ? (' - ' + m.name) : '')
51551                     );
51552             
51553          
51554             
51555             var disabled = (typeof(m.disabled) == 'function') ?
51556                 m.disabled.call(m.module.disabled) : m.disabled;    
51557             
51558             
51559             if (disabled) {
51560                 return progressRun(); // we do not update the display!
51561             }
51562             
51563             if (!m.parent || (typeof(m.parent) == 'string' && m.parent[0] == '#')) {
51564                 // it's a top level one..
51565                 var ctr = m.parent ? Roo.get(m.parent.substr(1)) : document.body;
51566                 if (!ctr) {
51567                     Roo.log("not rendering module " + m.name + " " + m.parent + " no found");
51568                      return progressRun.defer(10, _this);
51569                     
51570                 }
51571                 
51572                 
51573                 var layoutbase = new Ext.BorderLayout(
51574                     m.parent ? Roo.get(m.parent.substr(1)) : document.body,
51575                     {
51576                         center: {
51577                              titlebar: false,
51578                              autoScroll:false,
51579                              closeOnTab: true,
51580                              tabPosition: 'top',
51581                              //resizeTabs: true,
51582                              alwaysShowTabs: m.parent ? false : true,
51583                              hideTabs : m.parent ? true : false,
51584                              minTabWidth: 140
51585                         }
51586                 });
51587                 var tree = m.tree();
51588                 tree.region = 'center';
51589                 m.el = layoutbase.addxtype(tree);
51590                 m.panel = m.el;
51591                 m.layout = m.panel.layout;    
51592                 return progressRun.defer(10, _this);
51593             }
51594             
51595             var tree = m.tree();
51596             tree.region = tree.region || m.region;
51597             m.el = m.parent.el.addxtype(tree);
51598             m.fireEvent('built', m);
51599             m.panel = m.el;
51600             m.layout = m.panel.layout;    
51601             return progressRun.defer(10, _this); 
51602              
51603         }
51604         progressRun.defer(1, _this);
51605      
51606         
51607         
51608     }
51609      
51610    
51611     
51612     
51613 });
51614  //<script type="text/javascript">
51615
51616
51617 /**
51618  * @class Roo.Login
51619  * @extends Roo.LayoutDialog
51620  * A generic Login Dialog..... - only one needed in theory!?!?
51621  *
51622  * Fires XComponent builder on success...
51623  * 
51624  * Sends 
51625  *    username,password, lang = for login actions.
51626  *    check = 1 for periodic checking that sesion is valid.
51627  *    passwordRequest = email request password
51628  *    logout = 1 = to logout
51629  * 
51630  * Affects: (this id="????" elements)
51631  *   loading  (removed) (used to indicate application is loading)
51632  *   loading-mask (hides) (used to hide application when it's building loading)
51633  *   
51634  * 
51635  * Usage: 
51636  *    
51637  * 
51638  * Myapp.login = Roo.Login({
51639      url: xxxx,
51640    
51641      realm : 'Myapp', 
51642      
51643      
51644      method : 'POST',
51645      
51646      
51647      * 
51648  })
51649  * 
51650  * 
51651  * 
51652  **/
51653  
51654 Roo.Login = function(cfg)
51655 {
51656     this.addEvents({
51657         'refreshed' : true
51658     });
51659     
51660     Roo.apply(this,cfg);
51661     
51662     Roo.onReady(function() {
51663         this.onLoad();
51664     }, this);
51665     // call parent..
51666     
51667    
51668     Roo.Login.superclass.constructor.call(this, this);
51669     //this.addxtype(this.items[0]);
51670     
51671     
51672 }
51673
51674
51675 Roo.extend(Roo.Login, Roo.LayoutDialog, {
51676     
51677     /**
51678      * @cfg {String} method
51679      * Method used to query for login details.
51680      */
51681     
51682     method : 'POST',
51683     /**
51684      * @cfg {String} url
51685      * URL to query login data. - eg. baseURL + '/Login.php'
51686      */
51687     url : '',
51688     
51689     /**
51690      * @property user
51691      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
51692      * @type {Object} 
51693      */
51694     user : false,
51695     /**
51696      * @property checkFails
51697      * Number of times we have attempted to get authentication check, and failed.
51698      * @type {Number} 
51699      */
51700     checkFails : 0,
51701       /**
51702      * @property intervalID
51703      * The window interval that does the constant login checking.
51704      * @type {Number} 
51705      */
51706     intervalID : 0,
51707     
51708     
51709     onLoad : function() // called on page load...
51710     {
51711         // load 
51712          
51713         if (Roo.get('loading')) { // clear any loading indicator..
51714             Roo.get('loading').remove();
51715         }
51716         
51717         //this.switchLang('en'); // set the language to english..
51718        
51719         this.check({
51720             success:  function(response, opts)  {  // check successfull...
51721             
51722                 var res = this.processResponse(response);
51723                 this.checkFails =0;
51724                 if (!res.success) { // error!
51725                     this.checkFails = 5;
51726                     //console.log('call failure');
51727                     return this.failure(response,opts);
51728                 }
51729                 
51730                 if (!res.data.id) { // id=0 == login failure.
51731                     return this.show();
51732                 }
51733                 
51734                               
51735                         //console.log(success);
51736                 this.fillAuth(res.data);   
51737                 this.checkFails =0;
51738                 Roo.XComponent.build();
51739             },
51740             failure : this.show
51741         });
51742         
51743     }, 
51744     
51745     
51746     check: function(cfg) // called every so often to refresh cookie etc..
51747     {
51748         if (cfg.again) { // could be undefined..
51749             this.checkFails++;
51750         } else {
51751             this.checkFails = 0;
51752         }
51753         var _this = this;
51754         if (this.sending) {
51755             if ( this.checkFails > 4) {
51756                 Roo.MessageBox.alert("Error",  
51757                     "Error getting authentication status. - try reloading, or wait a while", function() {
51758                         _this.sending = false;
51759                     }); 
51760                 return;
51761             }
51762             cfg.again = true;
51763             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
51764             return;
51765         }
51766         this.sending = true;
51767         
51768         Roo.Ajax.request({  
51769             url: this.url,
51770             params: {
51771                 getAuthUser: true
51772             },  
51773             method: this.method,
51774             success:  cfg.success || this.success,
51775             failure : cfg.failure || this.failure,
51776             scope : this,
51777             callCfg : cfg
51778               
51779         });  
51780     }, 
51781     
51782     
51783     logout: function()
51784     {
51785         window.onbeforeunload = function() { }; // false does not work for IE..
51786         this.user = false;
51787         var _this = this;
51788         
51789         Roo.Ajax.request({  
51790             url: this.url,
51791             params: {
51792                 logout: 1
51793             },  
51794             method: 'GET',
51795             failure : function() {
51796                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
51797                     document.location = document.location.toString() + '?ts=' + Math.random();
51798                 });
51799                 
51800             },
51801             success : function() {
51802                 _this.user = false;
51803                 this.checkFails =0;
51804                 // fixme..
51805                 document.location = document.location.toString() + '?ts=' + Math.random();
51806             }
51807               
51808               
51809         }); 
51810     },
51811     
51812     processResponse : function (response)
51813     {
51814         var res = '';
51815         try {
51816             res = Roo.decode(response.responseText);
51817             // oops...
51818             if (typeof(res) != 'object') {
51819                 res = { success : false, errorMsg : res, errors : true };
51820             }
51821             if (typeof(res.success) == 'undefined') {
51822                 res.success = false;
51823             }
51824             
51825         } catch(e) {
51826             res = { success : false,  errorMsg : response.responseText, errors : true };
51827         }
51828         return res;
51829     },
51830     
51831     success : function(response, opts)  // check successfull...
51832     {  
51833         this.sending = false;
51834         var res = this.processResponse(response);
51835         if (!res.success) {
51836             return this.failure(response, opts);
51837         }
51838         if (!res.data || !res.data.id) {
51839             return this.failure(response,opts);
51840         }
51841         //console.log(res);
51842         this.fillAuth(res.data);
51843         
51844         this.checkFails =0;
51845         
51846     },
51847     
51848     
51849     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
51850     {
51851         this.authUser = -1;
51852         this.sending = false;
51853         var res = this.processResponse(response);
51854         //console.log(res);
51855         if ( this.checkFails > 2) {
51856         
51857             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
51858                 "Error getting authentication status. - try reloading"); 
51859             return;
51860         }
51861         opts.callCfg.again = true;
51862         this.check.defer(1000, this, [ opts.callCfg ]);
51863         return;  
51864     },
51865     
51866     
51867     
51868     fillAuth: function(au) {
51869         this.startAuthCheck();
51870         this.authUserId = au.id;
51871         this.authUser = au;
51872         this.lastChecked = new Date();
51873         this.fireEvent('refreshed', au);
51874         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
51875         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
51876         au.lang = au.lang || 'en';
51877         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
51878         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
51879         this.switchLang(au.lang );
51880         
51881      
51882         // open system... - -on setyp..
51883         if (this.authUserId  < 0) {
51884             Roo.MessageBox.alert("Warning", 
51885                 "This is an open system - please set up a admin user with a password.");  
51886         }
51887          
51888         //Pman.onload(); // which should do nothing if it's a re-auth result...
51889         
51890              
51891     },
51892     
51893     startAuthCheck : function() // starter for timeout checking..
51894     {
51895         if (this.intervalID) { // timer already in place...
51896             return false;
51897         }
51898         var _this = this;
51899         this.intervalID =  window.setInterval(function() {
51900               _this.check(false);
51901             }, 120000); // every 120 secs = 2mins..
51902         
51903         
51904     },
51905          
51906     
51907     switchLang : function (lang) 
51908     {
51909         _T = typeof(_T) == 'undefined' ? false : _T;
51910           if (!_T || !lang.length) {
51911             return;
51912         }
51913         
51914         if (!_T && lang != 'en') {
51915             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
51916             return;
51917         }
51918         
51919         if (typeof(_T.en) == 'undefined') {
51920             _T.en = {};
51921             Roo.apply(_T.en, _T);
51922         }
51923         
51924         if (typeof(_T[lang]) == 'undefined') {
51925             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
51926             return;
51927         }
51928         
51929         
51930         Roo.apply(_T, _T[lang]);
51931         // just need to set the text values for everything...
51932         var _this = this;
51933         /* this will not work ...
51934         if (this.form) { 
51935             
51936                
51937             function formLabel(name, val) {
51938                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
51939             }
51940             
51941             formLabel('password', "Password"+':');
51942             formLabel('username', "Email Address"+':');
51943             formLabel('lang', "Language"+':');
51944             this.dialog.setTitle("Login");
51945             this.dialog.buttons[0].setText("Forgot Password");
51946             this.dialog.buttons[1].setText("Login");
51947         }
51948         */
51949         
51950         
51951     },
51952     
51953     
51954     title: "Login",
51955     modal: true,
51956     width:  350,
51957     //height: 230,
51958     height: 180,
51959     shadow: true,
51960     minWidth:200,
51961     minHeight:180,
51962     //proxyDrag: true,
51963     closable: false,
51964     draggable: false,
51965     collapsible: false,
51966     resizable: false,
51967     center: {  // needed??
51968         autoScroll:false,
51969         titlebar: false,
51970        // tabPosition: 'top',
51971         hideTabs: true,
51972         closeOnTab: true,
51973         alwaysShowTabs: false
51974     } ,
51975     listeners : {
51976         
51977         show  : function(dlg)
51978         {
51979             //console.log(this);
51980             this.form = this.layout.getRegion('center').activePanel.form;
51981             this.form.dialog = dlg;
51982             this.buttons[0].form = this.form;
51983             this.buttons[0].dialog = dlg;
51984             this.buttons[1].form = this.form;
51985             this.buttons[1].dialog = dlg;
51986            
51987            //this.resizeToLogo.defer(1000,this);
51988             // this is all related to resizing for logos..
51989             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
51990            //// if (!sz) {
51991              //   this.resizeToLogo.defer(1000,this);
51992              //   return;
51993            // }
51994             //var w = Ext.lib.Dom.getViewWidth() - 100;
51995             //var h = Ext.lib.Dom.getViewHeight() - 100;
51996             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
51997             //this.center();
51998             if (this.disabled) {
51999                 this.hide();
52000                 return;
52001             }
52002             
52003             if (this.user.id < 0) { // used for inital setup situations.
52004                 return;
52005             }
52006             
52007             if (this.intervalID) {
52008                 // remove the timer
52009                 window.clearInterval(this.intervalID);
52010                 this.intervalID = false;
52011             }
52012             
52013             
52014             if (Roo.get('loading')) {
52015                 Roo.get('loading').remove();
52016             }
52017             if (Roo.get('loading-mask')) {
52018                 Roo.get('loading-mask').hide();
52019             }
52020             
52021             //incomming._node = tnode;
52022             this.form.reset();
52023             //this.dialog.modal = !modal;
52024             //this.dialog.show();
52025             this.el.unmask(); 
52026             
52027             
52028             this.form.setValues({
52029                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
52030                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
52031             });
52032             
52033             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
52034             if (this.form.findField('username').getValue().length > 0 ){
52035                 this.form.findField('password').focus();
52036             } else {
52037                this.form.findField('username').focus();
52038             }
52039     
52040         }
52041     },
52042     items : [
52043          {
52044        
52045             xtype : 'ContentPanel',
52046             xns : Roo,
52047             region: 'center',
52048             fitToFrame : true,
52049             
52050             items : [
52051     
52052                 {
52053                
52054                     xtype : 'Form',
52055                     xns : Roo.form,
52056                     labelWidth: 100,
52057                     style : 'margin: 10px;',
52058                     
52059                     listeners : {
52060                         actionfailed : function(f, act) {
52061                             // form can return { errors: .... }
52062                                 
52063                             //act.result.errors // invalid form element list...
52064                             //act.result.errorMsg// invalid form element list...
52065                             
52066                             this.dialog.el.unmask();
52067                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
52068                                         "Login failed - communication error - try again.");
52069                                       
52070                         },
52071                         actioncomplete: function(re, act) {
52072                              
52073                             Roo.state.Manager.set(
52074                                 this.dialog.realm + '.username',  
52075                                     this.findField('username').getValue()
52076                             );
52077                             Roo.state.Manager.set(
52078                                 this.dialog.realm + '.lang',  
52079                                 this.findField('lang').getValue() 
52080                             );
52081                             
52082                             this.dialog.fillAuth(act.result.data);
52083                               
52084                             this.dialog.hide();
52085                             
52086                             if (Roo.get('loading-mask')) {
52087                                 Roo.get('loading-mask').show();
52088                             }
52089                             Roo.XComponent.build();
52090                             
52091                              
52092                             
52093                         }
52094                     },
52095                     items : [
52096                         {
52097                             xtype : 'TextField',
52098                             xns : Roo.form,
52099                             fieldLabel: "Email Address",
52100                             name: 'username',
52101                             width:200,
52102                             autoCreate : {tag: "input", type: "text", size: "20"}
52103                         },
52104                         {
52105                             xtype : 'TextField',
52106                             xns : Roo.form,
52107                             fieldLabel: "Password",
52108                             inputType: 'password',
52109                             name: 'password',
52110                             width:200,
52111                             autoCreate : {tag: "input", type: "text", size: "20"},
52112                             listeners : {
52113                                 specialkey : function(e,ev) {
52114                                     if (ev.keyCode == 13) {
52115                                         this.form.dialog.el.mask("Logging in");
52116                                         this.form.doAction('submit', {
52117                                             url: this.form.dialog.url,
52118                                             method: this.form.dialog.method
52119                                         });
52120                                     }
52121                                 }
52122                             }  
52123                         },
52124                         {
52125                             xtype : 'ComboBox',
52126                             xns : Roo.form,
52127                             fieldLabel: "Language",
52128                             name : 'langdisp',
52129                             store: {
52130                                 xtype : 'SimpleStore',
52131                                 fields: ['lang', 'ldisp'],
52132                                 data : [
52133                                     [ 'en', 'English' ],
52134                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
52135                                     [ 'zh_CN', '\u7C21\u4E2D' ]
52136                                 ]
52137                             },
52138                             
52139                             valueField : 'lang',
52140                             hiddenName:  'lang',
52141                             width: 200,
52142                             displayField:'ldisp',
52143                             typeAhead: false,
52144                             editable: false,
52145                             mode: 'local',
52146                             triggerAction: 'all',
52147                             emptyText:'Select a Language...',
52148                             selectOnFocus:true,
52149                             listeners : {
52150                                 select :  function(cb, rec, ix) {
52151                                     this.form.switchLang(rec.data.lang);
52152                                 }
52153                             }
52154                         
52155                         }
52156                     ]
52157                 }
52158                   
52159                 
52160             ]
52161         }
52162     ],
52163     buttons : [
52164         {
52165             xtype : 'Button',
52166             xns : 'Roo',
52167             text : "Forgot Password",
52168             listeners : {
52169                 click : function() {
52170                     //console.log(this);
52171                     var n = this.form.findField('username').getValue();
52172                     if (!n.length) {
52173                         Roo.MessageBox.alert("Error", "Fill in your email address");
52174                         return;
52175                     }
52176                     Roo.Ajax.request({
52177                         url: this.dialog.url,
52178                         params: {
52179                             passwordRequest: n
52180                         },
52181                         method: this.dialog.method,
52182                         success:  function(response, opts)  {  // check successfull...
52183                         
52184                             var res = this.dialog.processResponse(response);
52185                             if (!res.success) { // error!
52186                                Roo.MessageBox.alert("Error" ,
52187                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
52188                                return;
52189                             }
52190                             Roo.MessageBox.alert("Notice" ,
52191                                 "Please check you email for the Password Reset message");
52192                         },
52193                         failure : function() {
52194                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
52195                         }
52196                         
52197                     });
52198                 }
52199             }
52200         },
52201         {
52202             xtype : 'Button',
52203             xns : 'Roo',
52204             text : "Login",
52205             listeners : {
52206                 
52207                 click : function () {
52208                         
52209                     this.dialog.el.mask("Logging in");
52210                     this.form.doAction('submit', {
52211                             url: this.dialog.url,
52212                             method: this.dialog.method
52213                     });
52214                 }
52215             }
52216         }
52217     ]
52218   
52219   
52220 })
52221  
52222
52223
52224