Prefer substring to using index for string tests
[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 // nasty IE9 hack - what a pile of crap that is..
4031
4032  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4033     Range.prototype.createContextualFragment = function (html) {
4034         var doc = window.document;
4035         var container = doc.createElement("div");
4036         container.innerHTML = html;
4037         var frag = doc.createDocumentFragment(), n;
4038         while ((n = container.firstChild)) {
4039             frag.appendChild(n);
4040         }
4041         return frag;
4042     };
4043 }
4044
4045 /**
4046  * @class Roo.DomHelper
4047  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4048  * 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>.
4049  * @singleton
4050  */
4051 Roo.DomHelper = function(){
4052     var tempTableEl = null;
4053     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4054     var tableRe = /^table|tbody|tr|td$/i;
4055     var xmlns = {};
4056     // build as innerHTML where available
4057     /** @ignore */
4058     var createHtml = function(o){
4059         if(typeof o == 'string'){
4060             return o;
4061         }
4062         var b = "";
4063         if(!o.tag){
4064             o.tag = "div";
4065         }
4066         b += "<" + o.tag;
4067         for(var attr in o){
4068             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4069             if(attr == "style"){
4070                 var s = o["style"];
4071                 if(typeof s == "function"){
4072                     s = s.call();
4073                 }
4074                 if(typeof s == "string"){
4075                     b += ' style="' + s + '"';
4076                 }else if(typeof s == "object"){
4077                     b += ' style="';
4078                     for(var key in s){
4079                         if(typeof s[key] != "function"){
4080                             b += key + ":" + s[key] + ";";
4081                         }
4082                     }
4083                     b += '"';
4084                 }
4085             }else{
4086                 if(attr == "cls"){
4087                     b += ' class="' + o["cls"] + '"';
4088                 }else if(attr == "htmlFor"){
4089                     b += ' for="' + o["htmlFor"] + '"';
4090                 }else{
4091                     b += " " + attr + '="' + o[attr] + '"';
4092                 }
4093             }
4094         }
4095         if(emptyTags.test(o.tag)){
4096             b += "/>";
4097         }else{
4098             b += ">";
4099             var cn = o.children || o.cn;
4100             if(cn){
4101                 //http://bugs.kde.org/show_bug.cgi?id=71506
4102                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4103                     for(var i = 0, len = cn.length; i < len; i++) {
4104                         b += createHtml(cn[i], b);
4105                     }
4106                 }else{
4107                     b += createHtml(cn, b);
4108                 }
4109             }
4110             if(o.html){
4111                 b += o.html;
4112             }
4113             b += "</" + o.tag + ">";
4114         }
4115         return b;
4116     };
4117
4118     // build as dom
4119     /** @ignore */
4120     var createDom = function(o, parentNode){
4121          
4122         // defininition craeted..
4123         var ns = false;
4124         if (o.ns && o.ns != 'html') {
4125                
4126             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4127                 xmlns[o.ns] = o.xmlns;
4128                 ns = o.xmlns;
4129             }
4130             if (typeof(xmlns[o.ns]) == 'undefined') {
4131                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4132             }
4133             ns = xmlns[o.ns];
4134         }
4135         
4136         
4137         if (typeof(o) == 'string') {
4138             return parentNode.appendChild(document.createTextNode(o));
4139         }
4140         o.tag = o.tag || div;
4141         if (o.ns && Roo.isIE) {
4142             ns = false;
4143             o.tag = o.ns + ':' + o.tag;
4144             
4145         }
4146         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4147         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4148         for(var attr in o){
4149             
4150             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4151                     attr == "style" || typeof o[attr] == "function") continue;
4152                     
4153             if(attr=="cls" && Roo.isIE){
4154                 el.className = o["cls"];
4155             }else{
4156                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4157                 else el[attr] = o[attr];
4158             }
4159         }
4160         Roo.DomHelper.applyStyles(el, o.style);
4161         var cn = o.children || o.cn;
4162         if(cn){
4163             //http://bugs.kde.org/show_bug.cgi?id=71506
4164              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4165                 for(var i = 0, len = cn.length; i < len; i++) {
4166                     createDom(cn[i], el);
4167                 }
4168             }else{
4169                 createDom(cn, el);
4170             }
4171         }
4172         if(o.html){
4173             el.innerHTML = o.html;
4174         }
4175         if(parentNode){
4176            parentNode.appendChild(el);
4177         }
4178         return el;
4179     };
4180
4181     var ieTable = function(depth, s, h, e){
4182         tempTableEl.innerHTML = [s, h, e].join('');
4183         var i = -1, el = tempTableEl;
4184         while(++i < depth){
4185             el = el.firstChild;
4186         }
4187         return el;
4188     };
4189
4190     // kill repeat to save bytes
4191     var ts = '<table>',
4192         te = '</table>',
4193         tbs = ts+'<tbody>',
4194         tbe = '</tbody>'+te,
4195         trs = tbs + '<tr>',
4196         tre = '</tr>'+tbe;
4197
4198     /**
4199      * @ignore
4200      * Nasty code for IE's broken table implementation
4201      */
4202     var insertIntoTable = function(tag, where, el, html){
4203         if(!tempTableEl){
4204             tempTableEl = document.createElement('div');
4205         }
4206         var node;
4207         var before = null;
4208         if(tag == 'td'){
4209             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4210                 return;
4211             }
4212             if(where == 'beforebegin'){
4213                 before = el;
4214                 el = el.parentNode;
4215             } else{
4216                 before = el.nextSibling;
4217                 el = el.parentNode;
4218             }
4219             node = ieTable(4, trs, html, tre);
4220         }
4221         else if(tag == 'tr'){
4222             if(where == 'beforebegin'){
4223                 before = el;
4224                 el = el.parentNode;
4225                 node = ieTable(3, tbs, html, tbe);
4226             } else if(where == 'afterend'){
4227                 before = el.nextSibling;
4228                 el = el.parentNode;
4229                 node = ieTable(3, tbs, html, tbe);
4230             } else{ // INTO a TR
4231                 if(where == 'afterbegin'){
4232                     before = el.firstChild;
4233                 }
4234                 node = ieTable(4, trs, html, tre);
4235             }
4236         } else if(tag == 'tbody'){
4237             if(where == 'beforebegin'){
4238                 before = el;
4239                 el = el.parentNode;
4240                 node = ieTable(2, ts, html, te);
4241             } else if(where == 'afterend'){
4242                 before = el.nextSibling;
4243                 el = el.parentNode;
4244                 node = ieTable(2, ts, html, te);
4245             } else{
4246                 if(where == 'afterbegin'){
4247                     before = el.firstChild;
4248                 }
4249                 node = ieTable(3, tbs, html, tbe);
4250             }
4251         } else{ // TABLE
4252             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4253                 return;
4254             }
4255             if(where == 'afterbegin'){
4256                 before = el.firstChild;
4257             }
4258             node = ieTable(2, ts, html, te);
4259         }
4260         el.insertBefore(node, before);
4261         return node;
4262     };
4263
4264     return {
4265     /** True to force the use of DOM instead of html fragments @type Boolean */
4266     useDom : false,
4267
4268     /**
4269      * Returns the markup for the passed Element(s) config
4270      * @param {Object} o The Dom object spec (and children)
4271      * @return {String}
4272      */
4273     markup : function(o){
4274         return createHtml(o);
4275     },
4276
4277     /**
4278      * Applies a style specification to an element
4279      * @param {String/HTMLElement} el The element to apply styles to
4280      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4281      * a function which returns such a specification.
4282      */
4283     applyStyles : function(el, styles){
4284         if(styles){
4285            el = Roo.fly(el);
4286            if(typeof styles == "string"){
4287                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4288                var matches;
4289                while ((matches = re.exec(styles)) != null){
4290                    el.setStyle(matches[1], matches[2]);
4291                }
4292            }else if (typeof styles == "object"){
4293                for (var style in styles){
4294                   el.setStyle(style, styles[style]);
4295                }
4296            }else if (typeof styles == "function"){
4297                 Roo.DomHelper.applyStyles(el, styles.call());
4298            }
4299         }
4300     },
4301
4302     /**
4303      * Inserts an HTML fragment into the Dom
4304      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4305      * @param {HTMLElement} el The context element
4306      * @param {String} html The HTML fragmenet
4307      * @return {HTMLElement} The new node
4308      */
4309     insertHtml : function(where, el, html){
4310         where = where.toLowerCase();
4311         if(el.insertAdjacentHTML){
4312             if(tableRe.test(el.tagName)){
4313                 var rs;
4314                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4315                     return rs;
4316                 }
4317             }
4318             switch(where){
4319                 case "beforebegin":
4320                     el.insertAdjacentHTML('BeforeBegin', html);
4321                     return el.previousSibling;
4322                 case "afterbegin":
4323                     el.insertAdjacentHTML('AfterBegin', html);
4324                     return el.firstChild;
4325                 case "beforeend":
4326                     el.insertAdjacentHTML('BeforeEnd', html);
4327                     return el.lastChild;
4328                 case "afterend":
4329                     el.insertAdjacentHTML('AfterEnd', html);
4330                     return el.nextSibling;
4331             }
4332             throw 'Illegal insertion point -> "' + where + '"';
4333         }
4334         var range = el.ownerDocument.createRange();
4335         var frag;
4336         switch(where){
4337              case "beforebegin":
4338                 range.setStartBefore(el);
4339                 frag = range.createContextualFragment(html);
4340                 el.parentNode.insertBefore(frag, el);
4341                 return el.previousSibling;
4342              case "afterbegin":
4343                 if(el.firstChild){
4344                     range.setStartBefore(el.firstChild);
4345                     frag = range.createContextualFragment(html);
4346                     el.insertBefore(frag, el.firstChild);
4347                     return el.firstChild;
4348                 }else{
4349                     el.innerHTML = html;
4350                     return el.firstChild;
4351                 }
4352             case "beforeend":
4353                 if(el.lastChild){
4354                     range.setStartAfter(el.lastChild);
4355                     frag = range.createContextualFragment(html);
4356                     el.appendChild(frag);
4357                     return el.lastChild;
4358                 }else{
4359                     el.innerHTML = html;
4360                     return el.lastChild;
4361                 }
4362             case "afterend":
4363                 range.setStartAfter(el);
4364                 frag = range.createContextualFragment(html);
4365                 el.parentNode.insertBefore(frag, el.nextSibling);
4366                 return el.nextSibling;
4367             }
4368             throw 'Illegal insertion point -> "' + where + '"';
4369     },
4370
4371     /**
4372      * Creates new Dom element(s) and inserts them before el
4373      * @param {String/HTMLElement/Element} el The context element
4374      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4375      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4376      * @return {HTMLElement/Roo.Element} The new node
4377      */
4378     insertBefore : function(el, o, returnElement){
4379         return this.doInsert(el, o, returnElement, "beforeBegin");
4380     },
4381
4382     /**
4383      * Creates new Dom element(s) and inserts them after el
4384      * @param {String/HTMLElement/Element} el The context element
4385      * @param {Object} o The Dom object spec (and children)
4386      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4387      * @return {HTMLElement/Roo.Element} The new node
4388      */
4389     insertAfter : function(el, o, returnElement){
4390         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4391     },
4392
4393     /**
4394      * Creates new Dom element(s) and inserts them as the first child of el
4395      * @param {String/HTMLElement/Element} el The context element
4396      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4397      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4398      * @return {HTMLElement/Roo.Element} The new node
4399      */
4400     insertFirst : function(el, o, returnElement){
4401         return this.doInsert(el, o, returnElement, "afterBegin");
4402     },
4403
4404     // private
4405     doInsert : function(el, o, returnElement, pos, sibling){
4406         el = Roo.getDom(el);
4407         var newNode;
4408         if(this.useDom || o.ns){
4409             newNode = createDom(o, null);
4410             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4411         }else{
4412             var html = createHtml(o);
4413             newNode = this.insertHtml(pos, el, html);
4414         }
4415         return returnElement ? Roo.get(newNode, true) : newNode;
4416     },
4417
4418     /**
4419      * Creates new Dom element(s) and appends them to el
4420      * @param {String/HTMLElement/Element} el The context element
4421      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4422      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4423      * @return {HTMLElement/Roo.Element} The new node
4424      */
4425     append : function(el, o, returnElement){
4426         el = Roo.getDom(el);
4427         var newNode;
4428         if(this.useDom || o.ns){
4429             newNode = createDom(o, null);
4430             el.appendChild(newNode);
4431         }else{
4432             var html = createHtml(o);
4433             newNode = this.insertHtml("beforeEnd", el, html);
4434         }
4435         return returnElement ? Roo.get(newNode, true) : newNode;
4436     },
4437
4438     /**
4439      * Creates new Dom element(s) and overwrites the contents of el with them
4440      * @param {String/HTMLElement/Element} el The context element
4441      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4442      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4443      * @return {HTMLElement/Roo.Element} The new node
4444      */
4445     overwrite : function(el, o, returnElement){
4446         el = Roo.getDom(el);
4447         if (o.ns) {
4448           
4449             while (el.childNodes.length) {
4450                 el.removeChild(el.firstChild);
4451             }
4452             createDom(o, el);
4453         } else {
4454             el.innerHTML = createHtml(o);   
4455         }
4456         
4457         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4458     },
4459
4460     /**
4461      * Creates a new Roo.DomHelper.Template from the Dom object spec
4462      * @param {Object} o The Dom object spec (and children)
4463      * @return {Roo.DomHelper.Template} The new template
4464      */
4465     createTemplate : function(o){
4466         var html = createHtml(o);
4467         return new Roo.Template(html);
4468     }
4469     };
4470 }();
4471 /*
4472  * Based on:
4473  * Ext JS Library 1.1.1
4474  * Copyright(c) 2006-2007, Ext JS, LLC.
4475  *
4476  * Originally Released Under LGPL - original licence link has changed is not relivant.
4477  *
4478  * Fork - LGPL
4479  * <script type="text/javascript">
4480  */
4481  
4482 /**
4483 * @class Roo.Template
4484 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4485 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4486 * Usage:
4487 <pre><code>
4488 var t = new Roo.Template({
4489     html :  '&lt;div name="{id}"&gt;' + 
4490         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4491         '&lt;/div&gt;',
4492     myformat: function (value, allValues) {
4493         return 'XX' + value;
4494     }
4495 });
4496 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4497 </code></pre>
4498 * 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>. 
4499 * @constructor
4500 * @param {Object} cfg - Configuration object.
4501 */
4502 Roo.Template = function(cfg){
4503     // BC!
4504     if(cfg instanceof Array){
4505         cfg = cfg.join("");
4506     }else if(arguments.length > 1){
4507         cfg = Array.prototype.join.call(arguments, "");
4508     }
4509     
4510     
4511     if (typeof(cfg) == 'object') {
4512         Roo.apply(this,cfg)
4513     } else {
4514         // bc
4515         this.html = cfg;
4516     }
4517     
4518     
4519 };
4520 Roo.Template.prototype = {
4521     
4522     /**
4523      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4524      */
4525     html : '',
4526     /**
4527      * Returns an HTML fragment of this template with the specified values applied.
4528      * @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'})
4529      * @return {String} The HTML fragment
4530      */
4531     applyTemplate : function(values){
4532         try {
4533             
4534             if(this.compiled){
4535                 return this.compiled(values);
4536             }
4537             var useF = this.disableFormats !== true;
4538             var fm = Roo.util.Format, tpl = this;
4539             var fn = function(m, name, format, args){
4540                 if(format && useF){
4541                     if(format.substr(0, 5) == "this."){
4542                         return tpl.call(format.substr(5), values[name], values);
4543                     }else{
4544                         if(args){
4545                             // quoted values are required for strings in compiled templates, 
4546                             // but for non compiled we need to strip them
4547                             // quoted reversed for jsmin
4548                             var re = /^\s*['"](.*)["']\s*$/;
4549                             args = args.split(',');
4550                             for(var i = 0, len = args.length; i < len; i++){
4551                                 args[i] = args[i].replace(re, "$1");
4552                             }
4553                             args = [values[name]].concat(args);
4554                         }else{
4555                             args = [values[name]];
4556                         }
4557                         return fm[format].apply(fm, args);
4558                     }
4559                 }else{
4560                     return values[name] !== undefined ? values[name] : "";
4561                 }
4562             };
4563             return this.html.replace(this.re, fn);
4564         } catch (e) {
4565             Roo.log(e);
4566             throw e;
4567         }
4568          
4569     },
4570     
4571     /**
4572      * Sets the HTML used as the template and optionally compiles it.
4573      * @param {String} html
4574      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4575      * @return {Roo.Template} this
4576      */
4577     set : function(html, compile){
4578         this.html = html;
4579         this.compiled = null;
4580         if(compile){
4581             this.compile();
4582         }
4583         return this;
4584     },
4585     
4586     /**
4587      * True to disable format functions (defaults to false)
4588      * @type Boolean
4589      */
4590     disableFormats : false,
4591     
4592     /**
4593     * The regular expression used to match template variables 
4594     * @type RegExp
4595     * @property 
4596     */
4597     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4598     
4599     /**
4600      * Compiles the template into an internal function, eliminating the RegEx overhead.
4601      * @return {Roo.Template} this
4602      */
4603     compile : function(){
4604         var fm = Roo.util.Format;
4605         var useF = this.disableFormats !== true;
4606         var sep = Roo.isGecko ? "+" : ",";
4607         var fn = function(m, name, format, args){
4608             if(format && useF){
4609                 args = args ? ',' + args : "";
4610                 if(format.substr(0, 5) != "this."){
4611                     format = "fm." + format + '(';
4612                 }else{
4613                     format = 'this.call("'+ format.substr(5) + '", ';
4614                     args = ", values";
4615                 }
4616             }else{
4617                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4618             }
4619             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4620         };
4621         var body;
4622         // branched to use + in gecko and [].join() in others
4623         if(Roo.isGecko){
4624             body = "this.compiled = function(values){ return '" +
4625                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4626                     "';};";
4627         }else{
4628             body = ["this.compiled = function(values){ return ['"];
4629             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4630             body.push("'].join('');};");
4631             body = body.join('');
4632         }
4633         /**
4634          * eval:var:values
4635          * eval:var:fm
4636          */
4637         eval(body);
4638         return this;
4639     },
4640     
4641     // private function used to call members
4642     call : function(fnName, value, allValues){
4643         return this[fnName](value, allValues);
4644     },
4645     
4646     /**
4647      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4648      * @param {String/HTMLElement/Roo.Element} el The context element
4649      * @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'})
4650      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4651      * @return {HTMLElement/Roo.Element} The new node or Element
4652      */
4653     insertFirst: function(el, values, returnElement){
4654         return this.doInsert('afterBegin', el, values, returnElement);
4655     },
4656
4657     /**
4658      * Applies the supplied values to the template and inserts the new node(s) before el.
4659      * @param {String/HTMLElement/Roo.Element} el The context element
4660      * @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'})
4661      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4662      * @return {HTMLElement/Roo.Element} The new node or Element
4663      */
4664     insertBefore: function(el, values, returnElement){
4665         return this.doInsert('beforeBegin', el, values, returnElement);
4666     },
4667
4668     /**
4669      * Applies the supplied values to the template and inserts the new node(s) after el.
4670      * @param {String/HTMLElement/Roo.Element} el The context element
4671      * @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'})
4672      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4673      * @return {HTMLElement/Roo.Element} The new node or Element
4674      */
4675     insertAfter : function(el, values, returnElement){
4676         return this.doInsert('afterEnd', el, values, returnElement);
4677     },
4678     
4679     /**
4680      * Applies the supplied values to the template and appends the new node(s) to el.
4681      * @param {String/HTMLElement/Roo.Element} el The context element
4682      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4683      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4684      * @return {HTMLElement/Roo.Element} The new node or Element
4685      */
4686     append : function(el, values, returnElement){
4687         return this.doInsert('beforeEnd', el, values, returnElement);
4688     },
4689
4690     doInsert : function(where, el, values, returnEl){
4691         el = Roo.getDom(el);
4692         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4693         return returnEl ? Roo.get(newNode, true) : newNode;
4694     },
4695
4696     /**
4697      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4698      * @param {String/HTMLElement/Roo.Element} el The context element
4699      * @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'})
4700      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4701      * @return {HTMLElement/Roo.Element} The new node or Element
4702      */
4703     overwrite : function(el, values, returnElement){
4704         el = Roo.getDom(el);
4705         el.innerHTML = this.applyTemplate(values);
4706         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4707     }
4708 };
4709 /**
4710  * Alias for {@link #applyTemplate}
4711  * @method
4712  */
4713 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4714
4715 // backwards compat
4716 Roo.DomHelper.Template = Roo.Template;
4717
4718 /**
4719  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4720  * @param {String/HTMLElement} el A DOM element or its id
4721  * @returns {Roo.Template} The created template
4722  * @static
4723  */
4724 Roo.Template.from = function(el){
4725     el = Roo.getDom(el);
4726     return new Roo.Template(el.value || el.innerHTML);
4727 };/*
4728  * Based on:
4729  * Ext JS Library 1.1.1
4730  * Copyright(c) 2006-2007, Ext JS, LLC.
4731  *
4732  * Originally Released Under LGPL - original licence link has changed is not relivant.
4733  *
4734  * Fork - LGPL
4735  * <script type="text/javascript">
4736  */
4737  
4738
4739 /*
4740  * This is code is also distributed under MIT license for use
4741  * with jQuery and prototype JavaScript libraries.
4742  */
4743 /**
4744  * @class Roo.DomQuery
4745 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).
4746 <p>
4747 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>
4748
4749 <p>
4750 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.
4751 </p>
4752 <h4>Element Selectors:</h4>
4753 <ul class="list">
4754     <li> <b>*</b> any element</li>
4755     <li> <b>E</b> an element with the tag E</li>
4756     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4757     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4758     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4759     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4760 </ul>
4761 <h4>Attribute Selectors:</h4>
4762 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4763 <ul class="list">
4764     <li> <b>E[foo]</b> has an attribute "foo"</li>
4765     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4766     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4767     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4768     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4769     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4770     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4771 </ul>
4772 <h4>Pseudo Classes:</h4>
4773 <ul class="list">
4774     <li> <b>E:first-child</b> E is the first child of its parent</li>
4775     <li> <b>E:last-child</b> E is the last child of its parent</li>
4776     <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>
4777     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4778     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4779     <li> <b>E:only-child</b> E is the only child of its parent</li>
4780     <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>
4781     <li> <b>E:first</b> the first E in the resultset</li>
4782     <li> <b>E:last</b> the last E in the resultset</li>
4783     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4784     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4785     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4786     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4787     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4788     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4789     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4790     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4791     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4792 </ul>
4793 <h4>CSS Value Selectors:</h4>
4794 <ul class="list">
4795     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4796     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4797     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4798     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4799     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4800     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4801 </ul>
4802  * @singleton
4803  */
4804 Roo.DomQuery = function(){
4805     var cache = {}, simpleCache = {}, valueCache = {};
4806     var nonSpace = /\S/;
4807     var trimRe = /^\s+|\s+$/g;
4808     var tplRe = /\{(\d+)\}/g;
4809     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4810     var tagTokenRe = /^(#)?([\w-\*]+)/;
4811     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4812
4813     function child(p, index){
4814         var i = 0;
4815         var n = p.firstChild;
4816         while(n){
4817             if(n.nodeType == 1){
4818                if(++i == index){
4819                    return n;
4820                }
4821             }
4822             n = n.nextSibling;
4823         }
4824         return null;
4825     };
4826
4827     function next(n){
4828         while((n = n.nextSibling) && n.nodeType != 1);
4829         return n;
4830     };
4831
4832     function prev(n){
4833         while((n = n.previousSibling) && n.nodeType != 1);
4834         return n;
4835     };
4836
4837     function children(d){
4838         var n = d.firstChild, ni = -1;
4839             while(n){
4840                 var nx = n.nextSibling;
4841                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4842                     d.removeChild(n);
4843                 }else{
4844                     n.nodeIndex = ++ni;
4845                 }
4846                 n = nx;
4847             }
4848             return this;
4849         };
4850
4851     function byClassName(c, a, v){
4852         if(!v){
4853             return c;
4854         }
4855         var r = [], ri = -1, cn;
4856         for(var i = 0, ci; ci = c[i]; i++){
4857             if((' '+ci.className+' ').indexOf(v) != -1){
4858                 r[++ri] = ci;
4859             }
4860         }
4861         return r;
4862     };
4863
4864     function attrValue(n, attr){
4865         if(!n.tagName && typeof n.length != "undefined"){
4866             n = n[0];
4867         }
4868         if(!n){
4869             return null;
4870         }
4871         if(attr == "for"){
4872             return n.htmlFor;
4873         }
4874         if(attr == "class" || attr == "className"){
4875             return n.className;
4876         }
4877         return n.getAttribute(attr) || n[attr];
4878
4879     };
4880
4881     function getNodes(ns, mode, tagName){
4882         var result = [], ri = -1, cs;
4883         if(!ns){
4884             return result;
4885         }
4886         tagName = tagName || "*";
4887         if(typeof ns.getElementsByTagName != "undefined"){
4888             ns = [ns];
4889         }
4890         if(!mode){
4891             for(var i = 0, ni; ni = ns[i]; i++){
4892                 cs = ni.getElementsByTagName(tagName);
4893                 for(var j = 0, ci; ci = cs[j]; j++){
4894                     result[++ri] = ci;
4895                 }
4896             }
4897         }else if(mode == "/" || mode == ">"){
4898             var utag = tagName.toUpperCase();
4899             for(var i = 0, ni, cn; ni = ns[i]; i++){
4900                 cn = ni.children || ni.childNodes;
4901                 for(var j = 0, cj; cj = cn[j]; j++){
4902                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4903                         result[++ri] = cj;
4904                     }
4905                 }
4906             }
4907         }else if(mode == "+"){
4908             var utag = tagName.toUpperCase();
4909             for(var i = 0, n; n = ns[i]; i++){
4910                 while((n = n.nextSibling) && n.nodeType != 1);
4911                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4912                     result[++ri] = n;
4913                 }
4914             }
4915         }else if(mode == "~"){
4916             for(var i = 0, n; n = ns[i]; i++){
4917                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4918                 if(n){
4919                     result[++ri] = n;
4920                 }
4921             }
4922         }
4923         return result;
4924     };
4925
4926     function concat(a, b){
4927         if(b.slice){
4928             return a.concat(b);
4929         }
4930         for(var i = 0, l = b.length; i < l; i++){
4931             a[a.length] = b[i];
4932         }
4933         return a;
4934     }
4935
4936     function byTag(cs, tagName){
4937         if(cs.tagName || cs == document){
4938             cs = [cs];
4939         }
4940         if(!tagName){
4941             return cs;
4942         }
4943         var r = [], ri = -1;
4944         tagName = tagName.toLowerCase();
4945         for(var i = 0, ci; ci = cs[i]; i++){
4946             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4947                 r[++ri] = ci;
4948             }
4949         }
4950         return r;
4951     };
4952
4953     function byId(cs, attr, id){
4954         if(cs.tagName || cs == document){
4955             cs = [cs];
4956         }
4957         if(!id){
4958             return cs;
4959         }
4960         var r = [], ri = -1;
4961         for(var i = 0,ci; ci = cs[i]; i++){
4962             if(ci && ci.id == id){
4963                 r[++ri] = ci;
4964                 return r;
4965             }
4966         }
4967         return r;
4968     };
4969
4970     function byAttribute(cs, attr, value, op, custom){
4971         var r = [], ri = -1, st = custom=="{";
4972         var f = Roo.DomQuery.operators[op];
4973         for(var i = 0, ci; ci = cs[i]; i++){
4974             var a;
4975             if(st){
4976                 a = Roo.DomQuery.getStyle(ci, attr);
4977             }
4978             else if(attr == "class" || attr == "className"){
4979                 a = ci.className;
4980             }else if(attr == "for"){
4981                 a = ci.htmlFor;
4982             }else if(attr == "href"){
4983                 a = ci.getAttribute("href", 2);
4984             }else{
4985                 a = ci.getAttribute(attr);
4986             }
4987             if((f && f(a, value)) || (!f && a)){
4988                 r[++ri] = ci;
4989             }
4990         }
4991         return r;
4992     };
4993
4994     function byPseudo(cs, name, value){
4995         return Roo.DomQuery.pseudos[name](cs, value);
4996     };
4997
4998     // This is for IE MSXML which does not support expandos.
4999     // IE runs the same speed using setAttribute, however FF slows way down
5000     // and Safari completely fails so they need to continue to use expandos.
5001     var isIE = window.ActiveXObject ? true : false;
5002
5003     // this eval is stop the compressor from
5004     // renaming the variable to something shorter
5005     
5006     /** eval:var:batch */
5007     var batch = 30803; 
5008
5009     var key = 30803;
5010
5011     function nodupIEXml(cs){
5012         var d = ++key;
5013         cs[0].setAttribute("_nodup", d);
5014         var r = [cs[0]];
5015         for(var i = 1, len = cs.length; i < len; i++){
5016             var c = cs[i];
5017             if(!c.getAttribute("_nodup") != d){
5018                 c.setAttribute("_nodup", d);
5019                 r[r.length] = c;
5020             }
5021         }
5022         for(var i = 0, len = cs.length; i < len; i++){
5023             cs[i].removeAttribute("_nodup");
5024         }
5025         return r;
5026     }
5027
5028     function nodup(cs){
5029         if(!cs){
5030             return [];
5031         }
5032         var len = cs.length, c, i, r = cs, cj, ri = -1;
5033         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5034             return cs;
5035         }
5036         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5037             return nodupIEXml(cs);
5038         }
5039         var d = ++key;
5040         cs[0]._nodup = d;
5041         for(i = 1; c = cs[i]; i++){
5042             if(c._nodup != d){
5043                 c._nodup = d;
5044             }else{
5045                 r = [];
5046                 for(var j = 0; j < i; j++){
5047                     r[++ri] = cs[j];
5048                 }
5049                 for(j = i+1; cj = cs[j]; j++){
5050                     if(cj._nodup != d){
5051                         cj._nodup = d;
5052                         r[++ri] = cj;
5053                     }
5054                 }
5055                 return r;
5056             }
5057         }
5058         return r;
5059     }
5060
5061     function quickDiffIEXml(c1, c2){
5062         var d = ++key;
5063         for(var i = 0, len = c1.length; i < len; i++){
5064             c1[i].setAttribute("_qdiff", d);
5065         }
5066         var r = [];
5067         for(var i = 0, len = c2.length; i < len; i++){
5068             if(c2[i].getAttribute("_qdiff") != d){
5069                 r[r.length] = c2[i];
5070             }
5071         }
5072         for(var i = 0, len = c1.length; i < len; i++){
5073            c1[i].removeAttribute("_qdiff");
5074         }
5075         return r;
5076     }
5077
5078     function quickDiff(c1, c2){
5079         var len1 = c1.length;
5080         if(!len1){
5081             return c2;
5082         }
5083         if(isIE && c1[0].selectSingleNode){
5084             return quickDiffIEXml(c1, c2);
5085         }
5086         var d = ++key;
5087         for(var i = 0; i < len1; i++){
5088             c1[i]._qdiff = d;
5089         }
5090         var r = [];
5091         for(var i = 0, len = c2.length; i < len; i++){
5092             if(c2[i]._qdiff != d){
5093                 r[r.length] = c2[i];
5094             }
5095         }
5096         return r;
5097     }
5098
5099     function quickId(ns, mode, root, id){
5100         if(ns == root){
5101            var d = root.ownerDocument || root;
5102            return d.getElementById(id);
5103         }
5104         ns = getNodes(ns, mode, "*");
5105         return byId(ns, null, id);
5106     }
5107
5108     return {
5109         getStyle : function(el, name){
5110             return Roo.fly(el).getStyle(name);
5111         },
5112         /**
5113          * Compiles a selector/xpath query into a reusable function. The returned function
5114          * takes one parameter "root" (optional), which is the context node from where the query should start.
5115          * @param {String} selector The selector/xpath query
5116          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5117          * @return {Function}
5118          */
5119         compile : function(path, type){
5120             type = type || "select";
5121             
5122             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5123             var q = path, mode, lq;
5124             var tk = Roo.DomQuery.matchers;
5125             var tklen = tk.length;
5126             var mm;
5127
5128             // accept leading mode switch
5129             var lmode = q.match(modeRe);
5130             if(lmode && lmode[1]){
5131                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5132                 q = q.replace(lmode[1], "");
5133             }
5134             // strip leading slashes
5135             while(path.substr(0, 1)=="/"){
5136                 path = path.substr(1);
5137             }
5138
5139             while(q && lq != q){
5140                 lq = q;
5141                 var tm = q.match(tagTokenRe);
5142                 if(type == "select"){
5143                     if(tm){
5144                         if(tm[1] == "#"){
5145                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5146                         }else{
5147                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5148                         }
5149                         q = q.replace(tm[0], "");
5150                     }else if(q.substr(0, 1) != '@'){
5151                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5152                     }
5153                 }else{
5154                     if(tm){
5155                         if(tm[1] == "#"){
5156                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5157                         }else{
5158                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5159                         }
5160                         q = q.replace(tm[0], "");
5161                     }
5162                 }
5163                 while(!(mm = q.match(modeRe))){
5164                     var matched = false;
5165                     for(var j = 0; j < tklen; j++){
5166                         var t = tk[j];
5167                         var m = q.match(t.re);
5168                         if(m){
5169                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5170                                                     return m[i];
5171                                                 });
5172                             q = q.replace(m[0], "");
5173                             matched = true;
5174                             break;
5175                         }
5176                     }
5177                     // prevent infinite loop on bad selector
5178                     if(!matched){
5179                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5180                     }
5181                 }
5182                 if(mm[1]){
5183                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5184                     q = q.replace(mm[1], "");
5185                 }
5186             }
5187             fn[fn.length] = "return nodup(n);\n}";
5188             
5189              /** 
5190               * list of variables that need from compression as they are used by eval.
5191              *  eval:var:batch 
5192              *  eval:var:nodup
5193              *  eval:var:byTag
5194              *  eval:var:ById
5195              *  eval:var:getNodes
5196              *  eval:var:quickId
5197              *  eval:var:mode
5198              *  eval:var:root
5199              *  eval:var:n
5200              *  eval:var:byClassName
5201              *  eval:var:byPseudo
5202              *  eval:var:byAttribute
5203              *  eval:var:attrValue
5204              * 
5205              **/ 
5206             eval(fn.join(""));
5207             return f;
5208         },
5209
5210         /**
5211          * Selects a group of elements.
5212          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5213          * @param {Node} root (optional) The start of the query (defaults to document).
5214          * @return {Array}
5215          */
5216         select : function(path, root, type){
5217             if(!root || root == document){
5218                 root = document;
5219             }
5220             if(typeof root == "string"){
5221                 root = document.getElementById(root);
5222             }
5223             var paths = path.split(",");
5224             var results = [];
5225             for(var i = 0, len = paths.length; i < len; i++){
5226                 var p = paths[i].replace(trimRe, "");
5227                 if(!cache[p]){
5228                     cache[p] = Roo.DomQuery.compile(p);
5229                     if(!cache[p]){
5230                         throw p + " is not a valid selector";
5231                     }
5232                 }
5233                 var result = cache[p](root);
5234                 if(result && result != document){
5235                     results = results.concat(result);
5236                 }
5237             }
5238             if(paths.length > 1){
5239                 return nodup(results);
5240             }
5241             return results;
5242         },
5243
5244         /**
5245          * Selects a single element.
5246          * @param {String} selector The selector/xpath query
5247          * @param {Node} root (optional) The start of the query (defaults to document).
5248          * @return {Element}
5249          */
5250         selectNode : function(path, root){
5251             return Roo.DomQuery.select(path, root)[0];
5252         },
5253
5254         /**
5255          * Selects the value of a node, optionally replacing null with the defaultValue.
5256          * @param {String} selector The selector/xpath query
5257          * @param {Node} root (optional) The start of the query (defaults to document).
5258          * @param {String} defaultValue
5259          */
5260         selectValue : function(path, root, defaultValue){
5261             path = path.replace(trimRe, "");
5262             if(!valueCache[path]){
5263                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5264             }
5265             var n = valueCache[path](root);
5266             n = n[0] ? n[0] : n;
5267             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5268             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5269         },
5270
5271         /**
5272          * Selects the value of a node, parsing integers and floats.
5273          * @param {String} selector The selector/xpath query
5274          * @param {Node} root (optional) The start of the query (defaults to document).
5275          * @param {Number} defaultValue
5276          * @return {Number}
5277          */
5278         selectNumber : function(path, root, defaultValue){
5279             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5280             return parseFloat(v);
5281         },
5282
5283         /**
5284          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5285          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5286          * @param {String} selector The simple selector to test
5287          * @return {Boolean}
5288          */
5289         is : function(el, ss){
5290             if(typeof el == "string"){
5291                 el = document.getElementById(el);
5292             }
5293             var isArray = (el instanceof Array);
5294             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5295             return isArray ? (result.length == el.length) : (result.length > 0);
5296         },
5297
5298         /**
5299          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5300          * @param {Array} el An array of elements to filter
5301          * @param {String} selector The simple selector to test
5302          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5303          * the selector instead of the ones that match
5304          * @return {Array}
5305          */
5306         filter : function(els, ss, nonMatches){
5307             ss = ss.replace(trimRe, "");
5308             if(!simpleCache[ss]){
5309                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5310             }
5311             var result = simpleCache[ss](els);
5312             return nonMatches ? quickDiff(result, els) : result;
5313         },
5314
5315         /**
5316          * Collection of matching regular expressions and code snippets.
5317          */
5318         matchers : [{
5319                 re: /^\.([\w-]+)/,
5320                 select: 'n = byClassName(n, null, " {1} ");'
5321             }, {
5322                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5323                 select: 'n = byPseudo(n, "{1}", "{2}");'
5324             },{
5325                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5326                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5327             }, {
5328                 re: /^#([\w-]+)/,
5329                 select: 'n = byId(n, null, "{1}");'
5330             },{
5331                 re: /^@([\w-]+)/,
5332                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5333             }
5334         ],
5335
5336         /**
5337          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5338          * 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;.
5339          */
5340         operators : {
5341             "=" : function(a, v){
5342                 return a == v;
5343             },
5344             "!=" : function(a, v){
5345                 return a != v;
5346             },
5347             "^=" : function(a, v){
5348                 return a && a.substr(0, v.length) == v;
5349             },
5350             "$=" : function(a, v){
5351                 return a && a.substr(a.length-v.length) == v;
5352             },
5353             "*=" : function(a, v){
5354                 return a && a.indexOf(v) !== -1;
5355             },
5356             "%=" : function(a, v){
5357                 return (a % v) == 0;
5358             },
5359             "|=" : function(a, v){
5360                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5361             },
5362             "~=" : function(a, v){
5363                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5364             }
5365         },
5366
5367         /**
5368          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5369          * and the argument (if any) supplied in the selector.
5370          */
5371         pseudos : {
5372             "first-child" : function(c){
5373                 var r = [], ri = -1, n;
5374                 for(var i = 0, ci; ci = n = c[i]; i++){
5375                     while((n = n.previousSibling) && n.nodeType != 1);
5376                     if(!n){
5377                         r[++ri] = ci;
5378                     }
5379                 }
5380                 return r;
5381             },
5382
5383             "last-child" : function(c){
5384                 var r = [], ri = -1, n;
5385                 for(var i = 0, ci; ci = n = c[i]; i++){
5386                     while((n = n.nextSibling) && n.nodeType != 1);
5387                     if(!n){
5388                         r[++ri] = ci;
5389                     }
5390                 }
5391                 return r;
5392             },
5393
5394             "nth-child" : function(c, a) {
5395                 var r = [], ri = -1;
5396                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5397                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5398                 for(var i = 0, n; n = c[i]; i++){
5399                     var pn = n.parentNode;
5400                     if (batch != pn._batch) {
5401                         var j = 0;
5402                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5403                             if(cn.nodeType == 1){
5404                                cn.nodeIndex = ++j;
5405                             }
5406                         }
5407                         pn._batch = batch;
5408                     }
5409                     if (f == 1) {
5410                         if (l == 0 || n.nodeIndex == l){
5411                             r[++ri] = n;
5412                         }
5413                     } else if ((n.nodeIndex + l) % f == 0){
5414                         r[++ri] = n;
5415                     }
5416                 }
5417
5418                 return r;
5419             },
5420
5421             "only-child" : function(c){
5422                 var r = [], ri = -1;;
5423                 for(var i = 0, ci; ci = c[i]; i++){
5424                     if(!prev(ci) && !next(ci)){
5425                         r[++ri] = ci;
5426                     }
5427                 }
5428                 return r;
5429             },
5430
5431             "empty" : function(c){
5432                 var r = [], ri = -1;
5433                 for(var i = 0, ci; ci = c[i]; i++){
5434                     var cns = ci.childNodes, j = 0, cn, empty = true;
5435                     while(cn = cns[j]){
5436                         ++j;
5437                         if(cn.nodeType == 1 || cn.nodeType == 3){
5438                             empty = false;
5439                             break;
5440                         }
5441                     }
5442                     if(empty){
5443                         r[++ri] = ci;
5444                     }
5445                 }
5446                 return r;
5447             },
5448
5449             "contains" : function(c, v){
5450                 var r = [], ri = -1;
5451                 for(var i = 0, ci; ci = c[i]; i++){
5452                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5453                         r[++ri] = ci;
5454                     }
5455                 }
5456                 return r;
5457             },
5458
5459             "nodeValue" : function(c, v){
5460                 var r = [], ri = -1;
5461                 for(var i = 0, ci; ci = c[i]; i++){
5462                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5463                         r[++ri] = ci;
5464                     }
5465                 }
5466                 return r;
5467             },
5468
5469             "checked" : function(c){
5470                 var r = [], ri = -1;
5471                 for(var i = 0, ci; ci = c[i]; i++){
5472                     if(ci.checked == true){
5473                         r[++ri] = ci;
5474                     }
5475                 }
5476                 return r;
5477             },
5478
5479             "not" : function(c, ss){
5480                 return Roo.DomQuery.filter(c, ss, true);
5481             },
5482
5483             "odd" : function(c){
5484                 return this["nth-child"](c, "odd");
5485             },
5486
5487             "even" : function(c){
5488                 return this["nth-child"](c, "even");
5489             },
5490
5491             "nth" : function(c, a){
5492                 return c[a-1] || [];
5493             },
5494
5495             "first" : function(c){
5496                 return c[0] || [];
5497             },
5498
5499             "last" : function(c){
5500                 return c[c.length-1] || [];
5501             },
5502
5503             "has" : function(c, ss){
5504                 var s = Roo.DomQuery.select;
5505                 var r = [], ri = -1;
5506                 for(var i = 0, ci; ci = c[i]; i++){
5507                     if(s(ss, ci).length > 0){
5508                         r[++ri] = ci;
5509                     }
5510                 }
5511                 return r;
5512             },
5513
5514             "next" : function(c, ss){
5515                 var is = Roo.DomQuery.is;
5516                 var r = [], ri = -1;
5517                 for(var i = 0, ci; ci = c[i]; i++){
5518                     var n = next(ci);
5519                     if(n && is(n, ss)){
5520                         r[++ri] = ci;
5521                     }
5522                 }
5523                 return r;
5524             },
5525
5526             "prev" : function(c, ss){
5527                 var is = Roo.DomQuery.is;
5528                 var r = [], ri = -1;
5529                 for(var i = 0, ci; ci = c[i]; i++){
5530                     var n = prev(ci);
5531                     if(n && is(n, ss)){
5532                         r[++ri] = ci;
5533                     }
5534                 }
5535                 return r;
5536             }
5537         }
5538     };
5539 }();
5540
5541 /**
5542  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5543  * @param {String} path The selector/xpath query
5544  * @param {Node} root (optional) The start of the query (defaults to document).
5545  * @return {Array}
5546  * @member Roo
5547  * @method query
5548  */
5549 Roo.query = Roo.DomQuery.select;
5550 /*
5551  * Based on:
5552  * Ext JS Library 1.1.1
5553  * Copyright(c) 2006-2007, Ext JS, LLC.
5554  *
5555  * Originally Released Under LGPL - original licence link has changed is not relivant.
5556  *
5557  * Fork - LGPL
5558  * <script type="text/javascript">
5559  */
5560
5561 /**
5562  * @class Roo.util.Observable
5563  * Base class that provides a common interface for publishing events. Subclasses are expected to
5564  * to have a property "events" with all the events defined.<br>
5565  * For example:
5566  * <pre><code>
5567  Employee = function(name){
5568     this.name = name;
5569     this.addEvents({
5570         "fired" : true,
5571         "quit" : true
5572     });
5573  }
5574  Roo.extend(Employee, Roo.util.Observable);
5575 </code></pre>
5576  * @param {Object} config properties to use (incuding events / listeners)
5577  */
5578
5579 Roo.util.Observable = function(cfg){
5580     
5581     cfg = cfg|| {};
5582     this.addEvents(cfg.events || {});
5583     if (cfg.events) {
5584         delete cfg.events; // make sure
5585     }
5586      
5587     Roo.apply(this, cfg);
5588     
5589     if(this.listeners){
5590         this.on(this.listeners);
5591         delete this.listeners;
5592     }
5593 };
5594 Roo.util.Observable.prototype = {
5595     /** 
5596  * @cfg {Object} listeners  list of events and functions to call for this object, 
5597  * For example :
5598  * <pre><code>
5599     listeners :  { 
5600        'click' : function(e) {
5601            ..... 
5602         } ,
5603         .... 
5604     } 
5605   </code></pre>
5606  */
5607     
5608     
5609     /**
5610      * Fires the specified event with the passed parameters (minus the event name).
5611      * @param {String} eventName
5612      * @param {Object...} args Variable number of parameters are passed to handlers
5613      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5614      */
5615     fireEvent : function(){
5616         var ce = this.events[arguments[0].toLowerCase()];
5617         if(typeof ce == "object"){
5618             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5619         }else{
5620             return true;
5621         }
5622     },
5623
5624     // private
5625     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5626
5627     /**
5628      * Appends an event handler to this component
5629      * @param {String}   eventName The type of event to listen for
5630      * @param {Function} handler The method the event invokes
5631      * @param {Object}   scope (optional) The scope in which to execute the handler
5632      * function. The handler function's "this" context.
5633      * @param {Object}   options (optional) An object containing handler configuration
5634      * properties. This may contain any of the following properties:<ul>
5635      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5636      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5637      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5638      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5639      * by the specified number of milliseconds. If the event fires again within that time, the original
5640      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5641      * </ul><br>
5642      * <p>
5643      * <b>Combining Options</b><br>
5644      * Using the options argument, it is possible to combine different types of listeners:<br>
5645      * <br>
5646      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5647                 <pre><code>
5648                 el.on('click', this.onClick, this, {
5649                         single: true,
5650                 delay: 100,
5651                 forumId: 4
5652                 });
5653                 </code></pre>
5654      * <p>
5655      * <b>Attaching multiple handlers in 1 call</b><br>
5656      * The method also allows for a single argument to be passed which is a config object containing properties
5657      * which specify multiple handlers.
5658      * <pre><code>
5659                 el.on({
5660                         'click': {
5661                         fn: this.onClick,
5662                         scope: this,
5663                         delay: 100
5664                 }, 
5665                 'mouseover': {
5666                         fn: this.onMouseOver,
5667                         scope: this
5668                 },
5669                 'mouseout': {
5670                         fn: this.onMouseOut,
5671                         scope: this
5672                 }
5673                 });
5674                 </code></pre>
5675      * <p>
5676      * Or a shorthand syntax which passes the same scope object to all handlers:
5677         <pre><code>
5678                 el.on({
5679                         'click': this.onClick,
5680                 'mouseover': this.onMouseOver,
5681                 'mouseout': this.onMouseOut,
5682                 scope: this
5683                 });
5684                 </code></pre>
5685      */
5686     addListener : function(eventName, fn, scope, o){
5687         if(typeof eventName == "object"){
5688             o = eventName;
5689             for(var e in o){
5690                 if(this.filterOptRe.test(e)){
5691                     continue;
5692                 }
5693                 if(typeof o[e] == "function"){
5694                     // shared options
5695                     this.addListener(e, o[e], o.scope,  o);
5696                 }else{
5697                     // individual options
5698                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5699                 }
5700             }
5701             return;
5702         }
5703         o = (!o || typeof o == "boolean") ? {} : o;
5704         eventName = eventName.toLowerCase();
5705         var ce = this.events[eventName] || true;
5706         if(typeof ce == "boolean"){
5707             ce = new Roo.util.Event(this, eventName);
5708             this.events[eventName] = ce;
5709         }
5710         ce.addListener(fn, scope, o);
5711     },
5712
5713     /**
5714      * Removes a listener
5715      * @param {String}   eventName     The type of event to listen for
5716      * @param {Function} handler        The handler to remove
5717      * @param {Object}   scope  (optional) The scope (this object) for the handler
5718      */
5719     removeListener : function(eventName, fn, scope){
5720         var ce = this.events[eventName.toLowerCase()];
5721         if(typeof ce == "object"){
5722             ce.removeListener(fn, scope);
5723         }
5724     },
5725
5726     /**
5727      * Removes all listeners for this object
5728      */
5729     purgeListeners : function(){
5730         for(var evt in this.events){
5731             if(typeof this.events[evt] == "object"){
5732                  this.events[evt].clearListeners();
5733             }
5734         }
5735     },
5736
5737     relayEvents : function(o, events){
5738         var createHandler = function(ename){
5739             return function(){
5740                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5741             };
5742         };
5743         for(var i = 0, len = events.length; i < len; i++){
5744             var ename = events[i];
5745             if(!this.events[ename]){ this.events[ename] = true; };
5746             o.on(ename, createHandler(ename), this);
5747         }
5748     },
5749
5750     /**
5751      * Used to define events on this Observable
5752      * @param {Object} object The object with the events defined
5753      */
5754     addEvents : function(o){
5755         if(!this.events){
5756             this.events = {};
5757         }
5758         Roo.applyIf(this.events, o);
5759     },
5760
5761     /**
5762      * Checks to see if this object has any listeners for a specified event
5763      * @param {String} eventName The name of the event to check for
5764      * @return {Boolean} True if the event is being listened for, else false
5765      */
5766     hasListener : function(eventName){
5767         var e = this.events[eventName];
5768         return typeof e == "object" && e.listeners.length > 0;
5769     }
5770 };
5771 /**
5772  * Appends an event handler to this element (shorthand for addListener)
5773  * @param {String}   eventName     The type of event to listen for
5774  * @param {Function} handler        The method the event invokes
5775  * @param {Object}   scope (optional) The scope in which to execute the handler
5776  * function. The handler function's "this" context.
5777  * @param {Object}   options  (optional)
5778  * @method
5779  */
5780 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5781 /**
5782  * Removes a listener (shorthand for removeListener)
5783  * @param {String}   eventName     The type of event to listen for
5784  * @param {Function} handler        The handler to remove
5785  * @param {Object}   scope  (optional) The scope (this object) for the handler
5786  * @method
5787  */
5788 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5789
5790 /**
5791  * Starts capture on the specified Observable. All events will be passed
5792  * to the supplied function with the event name + standard signature of the event
5793  * <b>before</b> the event is fired. If the supplied function returns false,
5794  * the event will not fire.
5795  * @param {Observable} o The Observable to capture
5796  * @param {Function} fn The function to call
5797  * @param {Object} scope (optional) The scope (this object) for the fn
5798  * @static
5799  */
5800 Roo.util.Observable.capture = function(o, fn, scope){
5801     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5802 };
5803
5804 /**
5805  * Removes <b>all</b> added captures from the Observable.
5806  * @param {Observable} o The Observable to release
5807  * @static
5808  */
5809 Roo.util.Observable.releaseCapture = function(o){
5810     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5811 };
5812
5813 (function(){
5814
5815     var createBuffered = function(h, o, scope){
5816         var task = new Roo.util.DelayedTask();
5817         return function(){
5818             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5819         };
5820     };
5821
5822     var createSingle = function(h, e, fn, scope){
5823         return function(){
5824             e.removeListener(fn, scope);
5825             return h.apply(scope, arguments);
5826         };
5827     };
5828
5829     var createDelayed = function(h, o, scope){
5830         return function(){
5831             var args = Array.prototype.slice.call(arguments, 0);
5832             setTimeout(function(){
5833                 h.apply(scope, args);
5834             }, o.delay || 10);
5835         };
5836     };
5837
5838     Roo.util.Event = function(obj, name){
5839         this.name = name;
5840         this.obj = obj;
5841         this.listeners = [];
5842     };
5843
5844     Roo.util.Event.prototype = {
5845         addListener : function(fn, scope, options){
5846             var o = options || {};
5847             scope = scope || this.obj;
5848             if(!this.isListening(fn, scope)){
5849                 var l = {fn: fn, scope: scope, options: o};
5850                 var h = fn;
5851                 if(o.delay){
5852                     h = createDelayed(h, o, scope);
5853                 }
5854                 if(o.single){
5855                     h = createSingle(h, this, fn, scope);
5856                 }
5857                 if(o.buffer){
5858                     h = createBuffered(h, o, scope);
5859                 }
5860                 l.fireFn = h;
5861                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5862                     this.listeners.push(l);
5863                 }else{
5864                     this.listeners = this.listeners.slice(0);
5865                     this.listeners.push(l);
5866                 }
5867             }
5868         },
5869
5870         findListener : function(fn, scope){
5871             scope = scope || this.obj;
5872             var ls = this.listeners;
5873             for(var i = 0, len = ls.length; i < len; i++){
5874                 var l = ls[i];
5875                 if(l.fn == fn && l.scope == scope){
5876                     return i;
5877                 }
5878             }
5879             return -1;
5880         },
5881
5882         isListening : function(fn, scope){
5883             return this.findListener(fn, scope) != -1;
5884         },
5885
5886         removeListener : function(fn, scope){
5887             var index;
5888             if((index = this.findListener(fn, scope)) != -1){
5889                 if(!this.firing){
5890                     this.listeners.splice(index, 1);
5891                 }else{
5892                     this.listeners = this.listeners.slice(0);
5893                     this.listeners.splice(index, 1);
5894                 }
5895                 return true;
5896             }
5897             return false;
5898         },
5899
5900         clearListeners : function(){
5901             this.listeners = [];
5902         },
5903
5904         fire : function(){
5905             var ls = this.listeners, scope, len = ls.length;
5906             if(len > 0){
5907                 this.firing = true;
5908                 var args = Array.prototype.slice.call(arguments, 0);
5909                 for(var i = 0; i < len; i++){
5910                     var l = ls[i];
5911                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5912                         this.firing = false;
5913                         return false;
5914                     }
5915                 }
5916                 this.firing = false;
5917             }
5918             return true;
5919         }
5920     };
5921 })();/*
5922  * Based on:
5923  * Ext JS Library 1.1.1
5924  * Copyright(c) 2006-2007, Ext JS, LLC.
5925  *
5926  * Originally Released Under LGPL - original licence link has changed is not relivant.
5927  *
5928  * Fork - LGPL
5929  * <script type="text/javascript">
5930  */
5931
5932 /**
5933  * @class Roo.EventManager
5934  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5935  * several useful events directly.
5936  * See {@link Roo.EventObject} for more details on normalized event objects.
5937  * @singleton
5938  */
5939 Roo.EventManager = function(){
5940     var docReadyEvent, docReadyProcId, docReadyState = false;
5941     var resizeEvent, resizeTask, textEvent, textSize;
5942     var E = Roo.lib.Event;
5943     var D = Roo.lib.Dom;
5944
5945
5946     var fireDocReady = function(){
5947         if(!docReadyState){
5948             docReadyState = true;
5949             Roo.isReady = true;
5950             if(docReadyProcId){
5951                 clearInterval(docReadyProcId);
5952             }
5953             if(Roo.isGecko || Roo.isOpera) {
5954                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5955             }
5956             if(Roo.isIE){
5957                 var defer = document.getElementById("ie-deferred-loader");
5958                 if(defer){
5959                     defer.onreadystatechange = null;
5960                     defer.parentNode.removeChild(defer);
5961                 }
5962             }
5963             if(docReadyEvent){
5964                 docReadyEvent.fire();
5965                 docReadyEvent.clearListeners();
5966             }
5967         }
5968     };
5969     
5970     var initDocReady = function(){
5971         docReadyEvent = new Roo.util.Event();
5972         if(Roo.isGecko || Roo.isOpera) {
5973             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5974         }else if(Roo.isIE){
5975             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5976             var defer = document.getElementById("ie-deferred-loader");
5977             defer.onreadystatechange = function(){
5978                 if(this.readyState == "complete"){
5979                     fireDocReady();
5980                 }
5981             };
5982         }else if(Roo.isSafari){ 
5983             docReadyProcId = setInterval(function(){
5984                 var rs = document.readyState;
5985                 if(rs == "complete") {
5986                     fireDocReady();     
5987                  }
5988             }, 10);
5989         }
5990         // no matter what, make sure it fires on load
5991         E.on(window, "load", fireDocReady);
5992     };
5993
5994     var createBuffered = function(h, o){
5995         var task = new Roo.util.DelayedTask(h);
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             task.delay(o.buffer, h, null, [e]);
6000         };
6001     };
6002
6003     var createSingle = function(h, el, ename, fn){
6004         return function(e){
6005             Roo.EventManager.removeListener(el, ename, fn);
6006             h(e);
6007         };
6008     };
6009
6010     var createDelayed = function(h, o){
6011         return function(e){
6012             // create new event object impl so new events don't wipe out properties
6013             e = new Roo.EventObjectImpl(e);
6014             setTimeout(function(){
6015                 h(e);
6016             }, o.delay || 10);
6017         };
6018     };
6019
6020     var listen = function(element, ename, opt, fn, scope){
6021         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6022         fn = fn || o.fn; scope = scope || o.scope;
6023         var el = Roo.getDom(element);
6024         if(!el){
6025             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6026         }
6027         var h = function(e){
6028             e = Roo.EventObject.setEvent(e);
6029             var t;
6030             if(o.delegate){
6031                 t = e.getTarget(o.delegate, el);
6032                 if(!t){
6033                     return;
6034                 }
6035             }else{
6036                 t = e.target;
6037             }
6038             if(o.stopEvent === true){
6039                 e.stopEvent();
6040             }
6041             if(o.preventDefault === true){
6042                e.preventDefault();
6043             }
6044             if(o.stopPropagation === true){
6045                 e.stopPropagation();
6046             }
6047
6048             if(o.normalized === false){
6049                 e = e.browserEvent;
6050             }
6051
6052             fn.call(scope || el, e, t, o);
6053         };
6054         if(o.delay){
6055             h = createDelayed(h, o);
6056         }
6057         if(o.single){
6058             h = createSingle(h, el, ename, fn);
6059         }
6060         if(o.buffer){
6061             h = createBuffered(h, o);
6062         }
6063         fn._handlers = fn._handlers || [];
6064         fn._handlers.push([Roo.id(el), ename, h]);
6065
6066         E.on(el, ename, h);
6067         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6068             el.addEventListener("DOMMouseScroll", h, false);
6069             E.on(window, 'unload', function(){
6070                 el.removeEventListener("DOMMouseScroll", h, false);
6071             });
6072         }
6073         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6074             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6075         }
6076         return h;
6077     };
6078
6079     var stopListening = function(el, ename, fn){
6080         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6081         if(hds){
6082             for(var i = 0, len = hds.length; i < len; i++){
6083                 var h = hds[i];
6084                 if(h[0] == id && h[1] == ename){
6085                     hd = h[2];
6086                     hds.splice(i, 1);
6087                     break;
6088                 }
6089             }
6090         }
6091         E.un(el, ename, hd);
6092         el = Roo.getDom(el);
6093         if(ename == "mousewheel" && el.addEventListener){
6094             el.removeEventListener("DOMMouseScroll", hd, false);
6095         }
6096         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6097             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6098         }
6099     };
6100
6101     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6102     
6103     var pub = {
6104         
6105         
6106         /** 
6107          * Fix for doc tools
6108          * @scope Roo.EventManager
6109          */
6110         
6111         
6112         /** 
6113          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6114          * object with a Roo.EventObject
6115          * @param {Function} fn        The method the event invokes
6116          * @param {Object}   scope    An object that becomes the scope of the handler
6117          * @param {boolean}  override If true, the obj passed in becomes
6118          *                             the execution scope of the listener
6119          * @return {Function} The wrapped function
6120          * @deprecated
6121          */
6122         wrap : function(fn, scope, override){
6123             return function(e){
6124                 Roo.EventObject.setEvent(e);
6125                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6126             };
6127         },
6128         
6129         /**
6130      * Appends an event handler to an element (shorthand for addListener)
6131      * @param {String/HTMLElement}   element        The html element or id to assign the
6132      * @param {String}   eventName The type of event to listen for
6133      * @param {Function} handler The method the event invokes
6134      * @param {Object}   scope (optional) The scope in which to execute the handler
6135      * function. The handler function's "this" context.
6136      * @param {Object}   options (optional) An object containing handler configuration
6137      * properties. This may contain any of the following properties:<ul>
6138      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6139      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6140      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6141      * <li>preventDefault {Boolean} True to prevent the default action</li>
6142      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6143      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6144      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6145      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6146      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6147      * by the specified number of milliseconds. If the event fires again within that time, the original
6148      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6149      * </ul><br>
6150      * <p>
6151      * <b>Combining Options</b><br>
6152      * Using the options argument, it is possible to combine different types of listeners:<br>
6153      * <br>
6154      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6155      * Code:<pre><code>
6156 el.on('click', this.onClick, this, {
6157     single: true,
6158     delay: 100,
6159     stopEvent : true,
6160     forumId: 4
6161 });</code></pre>
6162      * <p>
6163      * <b>Attaching multiple handlers in 1 call</b><br>
6164       * The method also allows for a single argument to be passed which is a config object containing properties
6165      * which specify multiple handlers.
6166      * <p>
6167      * Code:<pre><code>
6168 el.on({
6169     'click' : {
6170         fn: this.onClick
6171         scope: this,
6172         delay: 100
6173     },
6174     'mouseover' : {
6175         fn: this.onMouseOver
6176         scope: this
6177     },
6178     'mouseout' : {
6179         fn: this.onMouseOut
6180         scope: this
6181     }
6182 });</code></pre>
6183      * <p>
6184      * Or a shorthand syntax:<br>
6185      * Code:<pre><code>
6186 el.on({
6187     'click' : this.onClick,
6188     'mouseover' : this.onMouseOver,
6189     'mouseout' : this.onMouseOut
6190     scope: this
6191 });</code></pre>
6192      */
6193         addListener : function(element, eventName, fn, scope, options){
6194             if(typeof eventName == "object"){
6195                 var o = eventName;
6196                 for(var e in o){
6197                     if(propRe.test(e)){
6198                         continue;
6199                     }
6200                     if(typeof o[e] == "function"){
6201                         // shared options
6202                         listen(element, e, o, o[e], o.scope);
6203                     }else{
6204                         // individual options
6205                         listen(element, e, o[e]);
6206                     }
6207                 }
6208                 return;
6209             }
6210             return listen(element, eventName, options, fn, scope);
6211         },
6212         
6213         /**
6214          * Removes an event handler
6215          *
6216          * @param {String/HTMLElement}   element        The id or html element to remove the 
6217          *                             event from
6218          * @param {String}   eventName     The type of event
6219          * @param {Function} fn
6220          * @return {Boolean} True if a listener was actually removed
6221          */
6222         removeListener : function(element, eventName, fn){
6223             return stopListening(element, eventName, fn);
6224         },
6225         
6226         /**
6227          * Fires when the document is ready (before onload and before images are loaded). Can be 
6228          * accessed shorthanded Roo.onReady().
6229          * @param {Function} fn        The method the event invokes
6230          * @param {Object}   scope    An  object that becomes the scope of the handler
6231          * @param {boolean}  options
6232          */
6233         onDocumentReady : function(fn, scope, options){
6234             if(docReadyState){ // if it already fired
6235                 docReadyEvent.addListener(fn, scope, options);
6236                 docReadyEvent.fire();
6237                 docReadyEvent.clearListeners();
6238                 return;
6239             }
6240             if(!docReadyEvent){
6241                 initDocReady();
6242             }
6243             docReadyEvent.addListener(fn, scope, options);
6244         },
6245         
6246         /**
6247          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6248          * @param {Function} fn        The method the event invokes
6249          * @param {Object}   scope    An object that becomes the scope of the handler
6250          * @param {boolean}  options
6251          */
6252         onWindowResize : function(fn, scope, options){
6253             if(!resizeEvent){
6254                 resizeEvent = new Roo.util.Event();
6255                 resizeTask = new Roo.util.DelayedTask(function(){
6256                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6257                 });
6258                 E.on(window, "resize", function(){
6259                     if(Roo.isIE){
6260                         resizeTask.delay(50);
6261                     }else{
6262                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6263                     }
6264                 });
6265             }
6266             resizeEvent.addListener(fn, scope, options);
6267         },
6268
6269         /**
6270          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6271          * @param {Function} fn        The method the event invokes
6272          * @param {Object}   scope    An object that becomes the scope of the handler
6273          * @param {boolean}  options
6274          */
6275         onTextResize : function(fn, scope, options){
6276             if(!textEvent){
6277                 textEvent = new Roo.util.Event();
6278                 var textEl = new Roo.Element(document.createElement('div'));
6279                 textEl.dom.className = 'x-text-resize';
6280                 textEl.dom.innerHTML = 'X';
6281                 textEl.appendTo(document.body);
6282                 textSize = textEl.dom.offsetHeight;
6283                 setInterval(function(){
6284                     if(textEl.dom.offsetHeight != textSize){
6285                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6286                     }
6287                 }, this.textResizeInterval);
6288             }
6289             textEvent.addListener(fn, scope, options);
6290         },
6291
6292         /**
6293          * Removes the passed window resize listener.
6294          * @param {Function} fn        The method the event invokes
6295          * @param {Object}   scope    The scope of handler
6296          */
6297         removeResizeListener : function(fn, scope){
6298             if(resizeEvent){
6299                 resizeEvent.removeListener(fn, scope);
6300             }
6301         },
6302
6303         // private
6304         fireResize : function(){
6305             if(resizeEvent){
6306                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6307             }   
6308         },
6309         /**
6310          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6311          */
6312         ieDeferSrc : false,
6313         /**
6314          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6315          */
6316         textResizeInterval : 50
6317     };
6318     
6319     /**
6320      * Fix for doc tools
6321      * @scopeAlias pub=Roo.EventManager
6322      */
6323     
6324      /**
6325      * Appends an event handler to an element (shorthand for addListener)
6326      * @param {String/HTMLElement}   element        The html element or id to assign the
6327      * @param {String}   eventName The type of event to listen for
6328      * @param {Function} handler The method the event invokes
6329      * @param {Object}   scope (optional) The scope in which to execute the handler
6330      * function. The handler function's "this" context.
6331      * @param {Object}   options (optional) An object containing handler configuration
6332      * properties. This may contain any of the following properties:<ul>
6333      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6334      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6335      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6336      * <li>preventDefault {Boolean} True to prevent the default action</li>
6337      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6338      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6339      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6340      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6341      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6342      * by the specified number of milliseconds. If the event fires again within that time, the original
6343      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6344      * </ul><br>
6345      * <p>
6346      * <b>Combining Options</b><br>
6347      * Using the options argument, it is possible to combine different types of listeners:<br>
6348      * <br>
6349      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6350      * Code:<pre><code>
6351 el.on('click', this.onClick, this, {
6352     single: true,
6353     delay: 100,
6354     stopEvent : true,
6355     forumId: 4
6356 });</code></pre>
6357      * <p>
6358      * <b>Attaching multiple handlers in 1 call</b><br>
6359       * The method also allows for a single argument to be passed which is a config object containing properties
6360      * which specify multiple handlers.
6361      * <p>
6362      * Code:<pre><code>
6363 el.on({
6364     'click' : {
6365         fn: this.onClick
6366         scope: this,
6367         delay: 100
6368     },
6369     'mouseover' : {
6370         fn: this.onMouseOver
6371         scope: this
6372     },
6373     'mouseout' : {
6374         fn: this.onMouseOut
6375         scope: this
6376     }
6377 });</code></pre>
6378      * <p>
6379      * Or a shorthand syntax:<br>
6380      * Code:<pre><code>
6381 el.on({
6382     'click' : this.onClick,
6383     'mouseover' : this.onMouseOver,
6384     'mouseout' : this.onMouseOut
6385     scope: this
6386 });</code></pre>
6387      */
6388     pub.on = pub.addListener;
6389     pub.un = pub.removeListener;
6390
6391     pub.stoppedMouseDownEvent = new Roo.util.Event();
6392     return pub;
6393 }();
6394 /**
6395   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6396   * @param {Function} fn        The method the event invokes
6397   * @param {Object}   scope    An  object that becomes the scope of the handler
6398   * @param {boolean}  override If true, the obj passed in becomes
6399   *                             the execution scope of the listener
6400   * @member Roo
6401   * @method onReady
6402  */
6403 Roo.onReady = Roo.EventManager.onDocumentReady;
6404
6405 Roo.onReady(function(){
6406     var bd = Roo.get(document.body);
6407     if(!bd){ return; }
6408
6409     var cls = [
6410             Roo.isIE ? "roo-ie"
6411             : Roo.isGecko ? "roo-gecko"
6412             : Roo.isOpera ? "roo-opera"
6413             : Roo.isSafari ? "roo-safari" : ""];
6414
6415     if(Roo.isMac){
6416         cls.push("roo-mac");
6417     }
6418     if(Roo.isLinux){
6419         cls.push("roo-linux");
6420     }
6421     if(Roo.isBorderBox){
6422         cls.push('roo-border-box');
6423     }
6424     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6425         var p = bd.dom.parentNode;
6426         if(p){
6427             p.className += ' roo-strict';
6428         }
6429     }
6430     bd.addClass(cls.join(' '));
6431 });
6432
6433 /**
6434  * @class Roo.EventObject
6435  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6436  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6437  * Example:
6438  * <pre><code>
6439  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6440     e.preventDefault();
6441     var target = e.getTarget();
6442     ...
6443  }
6444  var myDiv = Roo.get("myDiv");
6445  myDiv.on("click", handleClick);
6446  //or
6447  Roo.EventManager.on("myDiv", 'click', handleClick);
6448  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6449  </code></pre>
6450  * @singleton
6451  */
6452 Roo.EventObject = function(){
6453     
6454     var E = Roo.lib.Event;
6455     
6456     // safari keypress events for special keys return bad keycodes
6457     var safariKeys = {
6458         63234 : 37, // left
6459         63235 : 39, // right
6460         63232 : 38, // up
6461         63233 : 40, // down
6462         63276 : 33, // page up
6463         63277 : 34, // page down
6464         63272 : 46, // delete
6465         63273 : 36, // home
6466         63275 : 35  // end
6467     };
6468
6469     // normalize button clicks
6470     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6471                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6472
6473     Roo.EventObjectImpl = function(e){
6474         if(e){
6475             this.setEvent(e.browserEvent || e);
6476         }
6477     };
6478     Roo.EventObjectImpl.prototype = {
6479         /**
6480          * Used to fix doc tools.
6481          * @scope Roo.EventObject.prototype
6482          */
6483             
6484
6485         
6486         
6487         /** The normal browser event */
6488         browserEvent : null,
6489         /** The button pressed in a mouse event */
6490         button : -1,
6491         /** True if the shift key was down during the event */
6492         shiftKey : false,
6493         /** True if the control key was down during the event */
6494         ctrlKey : false,
6495         /** True if the alt key was down during the event */
6496         altKey : false,
6497
6498         /** Key constant 
6499         * @type Number */
6500         BACKSPACE : 8,
6501         /** Key constant 
6502         * @type Number */
6503         TAB : 9,
6504         /** Key constant 
6505         * @type Number */
6506         RETURN : 13,
6507         /** Key constant 
6508         * @type Number */
6509         ENTER : 13,
6510         /** Key constant 
6511         * @type Number */
6512         SHIFT : 16,
6513         /** Key constant 
6514         * @type Number */
6515         CONTROL : 17,
6516         /** Key constant 
6517         * @type Number */
6518         ESC : 27,
6519         /** Key constant 
6520         * @type Number */
6521         SPACE : 32,
6522         /** Key constant 
6523         * @type Number */
6524         PAGEUP : 33,
6525         /** Key constant 
6526         * @type Number */
6527         PAGEDOWN : 34,
6528         /** Key constant 
6529         * @type Number */
6530         END : 35,
6531         /** Key constant 
6532         * @type Number */
6533         HOME : 36,
6534         /** Key constant 
6535         * @type Number */
6536         LEFT : 37,
6537         /** Key constant 
6538         * @type Number */
6539         UP : 38,
6540         /** Key constant 
6541         * @type Number */
6542         RIGHT : 39,
6543         /** Key constant 
6544         * @type Number */
6545         DOWN : 40,
6546         /** Key constant 
6547         * @type Number */
6548         DELETE : 46,
6549         /** Key constant 
6550         * @type Number */
6551         F5 : 116,
6552
6553            /** @private */
6554         setEvent : function(e){
6555             if(e == this || (e && e.browserEvent)){ // already wrapped
6556                 return e;
6557             }
6558             this.browserEvent = e;
6559             if(e){
6560                 // normalize buttons
6561                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6562                 if(e.type == 'click' && this.button == -1){
6563                     this.button = 0;
6564                 }
6565                 this.type = e.type;
6566                 this.shiftKey = e.shiftKey;
6567                 // mac metaKey behaves like ctrlKey
6568                 this.ctrlKey = e.ctrlKey || e.metaKey;
6569                 this.altKey = e.altKey;
6570                 // in getKey these will be normalized for the mac
6571                 this.keyCode = e.keyCode;
6572                 // keyup warnings on firefox.
6573                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6574                 // cache the target for the delayed and or buffered events
6575                 this.target = E.getTarget(e);
6576                 // same for XY
6577                 this.xy = E.getXY(e);
6578             }else{
6579                 this.button = -1;
6580                 this.shiftKey = false;
6581                 this.ctrlKey = false;
6582                 this.altKey = false;
6583                 this.keyCode = 0;
6584                 this.charCode =0;
6585                 this.target = null;
6586                 this.xy = [0, 0];
6587             }
6588             return this;
6589         },
6590
6591         /**
6592          * Stop the event (preventDefault and stopPropagation)
6593          */
6594         stopEvent : function(){
6595             if(this.browserEvent){
6596                 if(this.browserEvent.type == 'mousedown'){
6597                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6598                 }
6599                 E.stopEvent(this.browserEvent);
6600             }
6601         },
6602
6603         /**
6604          * Prevents the browsers default handling of the event.
6605          */
6606         preventDefault : function(){
6607             if(this.browserEvent){
6608                 E.preventDefault(this.browserEvent);
6609             }
6610         },
6611
6612         /** @private */
6613         isNavKeyPress : function(){
6614             var k = this.keyCode;
6615             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6616             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6617         },
6618
6619         isSpecialKey : function(){
6620             var k = this.keyCode;
6621             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6622             (k == 16) || (k == 17) ||
6623             (k >= 18 && k <= 20) ||
6624             (k >= 33 && k <= 35) ||
6625             (k >= 36 && k <= 39) ||
6626             (k >= 44 && k <= 45);
6627         },
6628         /**
6629          * Cancels bubbling of the event.
6630          */
6631         stopPropagation : function(){
6632             if(this.browserEvent){
6633                 if(this.type == 'mousedown'){
6634                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6635                 }
6636                 E.stopPropagation(this.browserEvent);
6637             }
6638         },
6639
6640         /**
6641          * Gets the key code for the event.
6642          * @return {Number}
6643          */
6644         getCharCode : function(){
6645             return this.charCode || this.keyCode;
6646         },
6647
6648         /**
6649          * Returns a normalized keyCode for the event.
6650          * @return {Number} The key code
6651          */
6652         getKey : function(){
6653             var k = this.keyCode || this.charCode;
6654             return Roo.isSafari ? (safariKeys[k] || k) : k;
6655         },
6656
6657         /**
6658          * Gets the x coordinate of the event.
6659          * @return {Number}
6660          */
6661         getPageX : function(){
6662             return this.xy[0];
6663         },
6664
6665         /**
6666          * Gets the y coordinate of the event.
6667          * @return {Number}
6668          */
6669         getPageY : function(){
6670             return this.xy[1];
6671         },
6672
6673         /**
6674          * Gets the time of the event.
6675          * @return {Number}
6676          */
6677         getTime : function(){
6678             if(this.browserEvent){
6679                 return E.getTime(this.browserEvent);
6680             }
6681             return null;
6682         },
6683
6684         /**
6685          * Gets the page coordinates of the event.
6686          * @return {Array} The xy values like [x, y]
6687          */
6688         getXY : function(){
6689             return this.xy;
6690         },
6691
6692         /**
6693          * Gets the target for the event.
6694          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6695          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6696                 search as a number or element (defaults to 10 || document.body)
6697          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6698          * @return {HTMLelement}
6699          */
6700         getTarget : function(selector, maxDepth, returnEl){
6701             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6702         },
6703         /**
6704          * Gets the related target.
6705          * @return {HTMLElement}
6706          */
6707         getRelatedTarget : function(){
6708             if(this.browserEvent){
6709                 return E.getRelatedTarget(this.browserEvent);
6710             }
6711             return null;
6712         },
6713
6714         /**
6715          * Normalizes mouse wheel delta across browsers
6716          * @return {Number} The delta
6717          */
6718         getWheelDelta : function(){
6719             var e = this.browserEvent;
6720             var delta = 0;
6721             if(e.wheelDelta){ /* IE/Opera. */
6722                 delta = e.wheelDelta/120;
6723             }else if(e.detail){ /* Mozilla case. */
6724                 delta = -e.detail/3;
6725             }
6726             return delta;
6727         },
6728
6729         /**
6730          * Returns true if the control, meta, shift or alt key was pressed during this event.
6731          * @return {Boolean}
6732          */
6733         hasModifier : function(){
6734             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6735         },
6736
6737         /**
6738          * Returns true if the target of this event equals el or is a child of el
6739          * @param {String/HTMLElement/Element} el
6740          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6741          * @return {Boolean}
6742          */
6743         within : function(el, related){
6744             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6745             return t && Roo.fly(el).contains(t);
6746         },
6747
6748         getPoint : function(){
6749             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6750         }
6751     };
6752
6753     return new Roo.EventObjectImpl();
6754 }();
6755             
6756     /*
6757  * Based on:
6758  * Ext JS Library 1.1.1
6759  * Copyright(c) 2006-2007, Ext JS, LLC.
6760  *
6761  * Originally Released Under LGPL - original licence link has changed is not relivant.
6762  *
6763  * Fork - LGPL
6764  * <script type="text/javascript">
6765  */
6766
6767  
6768 // was in Composite Element!??!?!
6769  
6770 (function(){
6771     var D = Roo.lib.Dom;
6772     var E = Roo.lib.Event;
6773     var A = Roo.lib.Anim;
6774
6775     // local style camelizing for speed
6776     var propCache = {};
6777     var camelRe = /(-[a-z])/gi;
6778     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6779     var view = document.defaultView;
6780
6781 /**
6782  * @class Roo.Element
6783  * Represents an Element in the DOM.<br><br>
6784  * Usage:<br>
6785 <pre><code>
6786 var el = Roo.get("my-div");
6787
6788 // or with getEl
6789 var el = getEl("my-div");
6790
6791 // or with a DOM element
6792 var el = Roo.get(myDivElement);
6793 </code></pre>
6794  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6795  * each call instead of constructing a new one.<br><br>
6796  * <b>Animations</b><br />
6797  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6798  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6799 <pre>
6800 Option    Default   Description
6801 --------- --------  ---------------------------------------------
6802 duration  .35       The duration of the animation in seconds
6803 easing    easeOut   The YUI easing method
6804 callback  none      A function to execute when the anim completes
6805 scope     this      The scope (this) of the callback function
6806 </pre>
6807 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6808 * manipulate the animation. Here's an example:
6809 <pre><code>
6810 var el = Roo.get("my-div");
6811
6812 // no animation
6813 el.setWidth(100);
6814
6815 // default animation
6816 el.setWidth(100, true);
6817
6818 // animation with some options set
6819 el.setWidth(100, {
6820     duration: 1,
6821     callback: this.foo,
6822     scope: this
6823 });
6824
6825 // using the "anim" property to get the Anim object
6826 var opt = {
6827     duration: 1,
6828     callback: this.foo,
6829     scope: this
6830 };
6831 el.setWidth(100, opt);
6832 ...
6833 if(opt.anim.isAnimated()){
6834     opt.anim.stop();
6835 }
6836 </code></pre>
6837 * <b> Composite (Collections of) Elements</b><br />
6838  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6839  * @constructor Create a new Element directly.
6840  * @param {String/HTMLElement} element
6841  * @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).
6842  */
6843     Roo.Element = function(element, forceNew){
6844         var dom = typeof element == "string" ?
6845                 document.getElementById(element) : element;
6846         if(!dom){ // invalid id/element
6847             return null;
6848         }
6849         var id = dom.id;
6850         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6851             return Roo.Element.cache[id];
6852         }
6853
6854         /**
6855          * The DOM element
6856          * @type HTMLElement
6857          */
6858         this.dom = dom;
6859
6860         /**
6861          * The DOM element ID
6862          * @type String
6863          */
6864         this.id = id || Roo.id(dom);
6865     };
6866
6867     var El = Roo.Element;
6868
6869     El.prototype = {
6870         /**
6871          * The element's default display mode  (defaults to "")
6872          * @type String
6873          */
6874         originalDisplay : "",
6875
6876         visibilityMode : 1,
6877         /**
6878          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6879          * @type String
6880          */
6881         defaultUnit : "px",
6882         /**
6883          * Sets the element's visibility mode. When setVisible() is called it
6884          * will use this to determine whether to set the visibility or the display property.
6885          * @param visMode Element.VISIBILITY or Element.DISPLAY
6886          * @return {Roo.Element} this
6887          */
6888         setVisibilityMode : function(visMode){
6889             this.visibilityMode = visMode;
6890             return this;
6891         },
6892         /**
6893          * Convenience method for setVisibilityMode(Element.DISPLAY)
6894          * @param {String} display (optional) What to set display to when visible
6895          * @return {Roo.Element} this
6896          */
6897         enableDisplayMode : function(display){
6898             this.setVisibilityMode(El.DISPLAY);
6899             if(typeof display != "undefined") this.originalDisplay = display;
6900             return this;
6901         },
6902
6903         /**
6904          * 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)
6905          * @param {String} selector The simple selector to test
6906          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6907                 search as a number or element (defaults to 10 || document.body)
6908          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6909          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6910          */
6911         findParent : function(simpleSelector, maxDepth, returnEl){
6912             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6913             maxDepth = maxDepth || 50;
6914             if(typeof maxDepth != "number"){
6915                 stopEl = Roo.getDom(maxDepth);
6916                 maxDepth = 10;
6917             }
6918             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6919                 if(dq.is(p, simpleSelector)){
6920                     return returnEl ? Roo.get(p) : p;
6921                 }
6922                 depth++;
6923                 p = p.parentNode;
6924             }
6925             return null;
6926         },
6927
6928
6929         /**
6930          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6931          * @param {String} selector The simple selector to test
6932          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6933                 search as a number or element (defaults to 10 || document.body)
6934          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6935          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6936          */
6937         findParentNode : function(simpleSelector, maxDepth, returnEl){
6938             var p = Roo.fly(this.dom.parentNode, '_internal');
6939             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6940         },
6941
6942         /**
6943          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6944          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6945          * @param {String} selector The simple selector to test
6946          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6947                 search as a number or element (defaults to 10 || document.body)
6948          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6949          */
6950         up : function(simpleSelector, maxDepth){
6951             return this.findParentNode(simpleSelector, maxDepth, true);
6952         },
6953
6954
6955
6956         /**
6957          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6958          * @param {String} selector The simple selector to test
6959          * @return {Boolean} True if this element matches the selector, else false
6960          */
6961         is : function(simpleSelector){
6962             return Roo.DomQuery.is(this.dom, simpleSelector);
6963         },
6964
6965         /**
6966          * Perform animation on this element.
6967          * @param {Object} args The YUI animation control args
6968          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6969          * @param {Function} onComplete (optional) Function to call when animation completes
6970          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6971          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6972          * @return {Roo.Element} this
6973          */
6974         animate : function(args, duration, onComplete, easing, animType){
6975             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6976             return this;
6977         },
6978
6979         /*
6980          * @private Internal animation call
6981          */
6982         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6983             animType = animType || 'run';
6984             opt = opt || {};
6985             var anim = Roo.lib.Anim[animType](
6986                 this.dom, args,
6987                 (opt.duration || defaultDur) || .35,
6988                 (opt.easing || defaultEase) || 'easeOut',
6989                 function(){
6990                     Roo.callback(cb, this);
6991                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6992                 },
6993                 this
6994             );
6995             opt.anim = anim;
6996             return anim;
6997         },
6998
6999         // private legacy anim prep
7000         preanim : function(a, i){
7001             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7002         },
7003
7004         /**
7005          * Removes worthless text nodes
7006          * @param {Boolean} forceReclean (optional) By default the element
7007          * keeps track if it has been cleaned already so
7008          * you can call this over and over. However, if you update the element and
7009          * need to force a reclean, you can pass true.
7010          */
7011         clean : function(forceReclean){
7012             if(this.isCleaned && forceReclean !== true){
7013                 return this;
7014             }
7015             var ns = /\S/;
7016             var d = this.dom, n = d.firstChild, ni = -1;
7017             while(n){
7018                 var nx = n.nextSibling;
7019                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7020                     d.removeChild(n);
7021                 }else{
7022                     n.nodeIndex = ++ni;
7023                 }
7024                 n = nx;
7025             }
7026             this.isCleaned = true;
7027             return this;
7028         },
7029
7030         // private
7031         calcOffsetsTo : function(el){
7032             el = Roo.get(el);
7033             var d = el.dom;
7034             var restorePos = false;
7035             if(el.getStyle('position') == 'static'){
7036                 el.position('relative');
7037                 restorePos = true;
7038             }
7039             var x = 0, y =0;
7040             var op = this.dom;
7041             while(op && op != d && op.tagName != 'HTML'){
7042                 x+= op.offsetLeft;
7043                 y+= op.offsetTop;
7044                 op = op.offsetParent;
7045             }
7046             if(restorePos){
7047                 el.position('static');
7048             }
7049             return [x, y];
7050         },
7051
7052         /**
7053          * Scrolls this element into view within the passed container.
7054          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7055          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7056          * @return {Roo.Element} this
7057          */
7058         scrollIntoView : function(container, hscroll){
7059             var c = Roo.getDom(container) || document.body;
7060             var el = this.dom;
7061
7062             var o = this.calcOffsetsTo(c),
7063                 l = o[0],
7064                 t = o[1],
7065                 b = t+el.offsetHeight,
7066                 r = l+el.offsetWidth;
7067
7068             var ch = c.clientHeight;
7069             var ct = parseInt(c.scrollTop, 10);
7070             var cl = parseInt(c.scrollLeft, 10);
7071             var cb = ct + ch;
7072             var cr = cl + c.clientWidth;
7073
7074             if(t < ct){
7075                 c.scrollTop = t;
7076             }else if(b > cb){
7077                 c.scrollTop = b-ch;
7078             }
7079
7080             if(hscroll !== false){
7081                 if(l < cl){
7082                     c.scrollLeft = l;
7083                 }else if(r > cr){
7084                     c.scrollLeft = r-c.clientWidth;
7085                 }
7086             }
7087             return this;
7088         },
7089
7090         // private
7091         scrollChildIntoView : function(child, hscroll){
7092             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7093         },
7094
7095         /**
7096          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7097          * the new height may not be available immediately.
7098          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7099          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7100          * @param {Function} onComplete (optional) Function to call when animation completes
7101          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7102          * @return {Roo.Element} this
7103          */
7104         autoHeight : function(animate, duration, onComplete, easing){
7105             var oldHeight = this.getHeight();
7106             this.clip();
7107             this.setHeight(1); // force clipping
7108             setTimeout(function(){
7109                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7110                 if(!animate){
7111                     this.setHeight(height);
7112                     this.unclip();
7113                     if(typeof onComplete == "function"){
7114                         onComplete();
7115                     }
7116                 }else{
7117                     this.setHeight(oldHeight); // restore original height
7118                     this.setHeight(height, animate, duration, function(){
7119                         this.unclip();
7120                         if(typeof onComplete == "function") onComplete();
7121                     }.createDelegate(this), easing);
7122                 }
7123             }.createDelegate(this), 0);
7124             return this;
7125         },
7126
7127         /**
7128          * Returns true if this element is an ancestor of the passed element
7129          * @param {HTMLElement/String} el The element to check
7130          * @return {Boolean} True if this element is an ancestor of el, else false
7131          */
7132         contains : function(el){
7133             if(!el){return false;}
7134             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7135         },
7136
7137         /**
7138          * Checks whether the element is currently visible using both visibility and display properties.
7139          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7140          * @return {Boolean} True if the element is currently visible, else false
7141          */
7142         isVisible : function(deep) {
7143             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7144             if(deep !== true || !vis){
7145                 return vis;
7146             }
7147             var p = this.dom.parentNode;
7148             while(p && p.tagName.toLowerCase() != "body"){
7149                 if(!Roo.fly(p, '_isVisible').isVisible()){
7150                     return false;
7151                 }
7152                 p = p.parentNode;
7153             }
7154             return true;
7155         },
7156
7157         /**
7158          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7159          * @param {String} selector The CSS selector
7160          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7161          * @return {CompositeElement/CompositeElementLite} The composite element
7162          */
7163         select : function(selector, unique){
7164             return El.select(selector, unique, this.dom);
7165         },
7166
7167         /**
7168          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7169          * @param {String} selector The CSS selector
7170          * @return {Array} An array of the matched nodes
7171          */
7172         query : function(selector, unique){
7173             return Roo.DomQuery.select(selector, this.dom);
7174         },
7175
7176         /**
7177          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7178          * @param {String} selector The CSS selector
7179          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7180          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7181          */
7182         child : function(selector, returnDom){
7183             var n = Roo.DomQuery.selectNode(selector, this.dom);
7184             return returnDom ? n : Roo.get(n);
7185         },
7186
7187         /**
7188          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7189          * @param {String} selector The CSS selector
7190          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7191          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7192          */
7193         down : function(selector, returnDom){
7194             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7195             return returnDom ? n : Roo.get(n);
7196         },
7197
7198         /**
7199          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7200          * @param {String} group The group the DD object is member of
7201          * @param {Object} config The DD config object
7202          * @param {Object} overrides An object containing methods to override/implement on the DD object
7203          * @return {Roo.dd.DD} The DD object
7204          */
7205         initDD : function(group, config, overrides){
7206             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7207             return Roo.apply(dd, overrides);
7208         },
7209
7210         /**
7211          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7212          * @param {String} group The group the DDProxy object is member of
7213          * @param {Object} config The DDProxy config object
7214          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7215          * @return {Roo.dd.DDProxy} The DDProxy object
7216          */
7217         initDDProxy : function(group, config, overrides){
7218             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7219             return Roo.apply(dd, overrides);
7220         },
7221
7222         /**
7223          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7224          * @param {String} group The group the DDTarget object is member of
7225          * @param {Object} config The DDTarget config object
7226          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7227          * @return {Roo.dd.DDTarget} The DDTarget object
7228          */
7229         initDDTarget : function(group, config, overrides){
7230             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7231             return Roo.apply(dd, overrides);
7232         },
7233
7234         /**
7235          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7236          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7237          * @param {Boolean} visible Whether the element is visible
7238          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7239          * @return {Roo.Element} this
7240          */
7241          setVisible : function(visible, animate){
7242             if(!animate || !A){
7243                 if(this.visibilityMode == El.DISPLAY){
7244                     this.setDisplayed(visible);
7245                 }else{
7246                     this.fixDisplay();
7247                     this.dom.style.visibility = visible ? "visible" : "hidden";
7248                 }
7249             }else{
7250                 // closure for composites
7251                 var dom = this.dom;
7252                 var visMode = this.visibilityMode;
7253                 if(visible){
7254                     this.setOpacity(.01);
7255                     this.setVisible(true);
7256                 }
7257                 this.anim({opacity: { to: (visible?1:0) }},
7258                       this.preanim(arguments, 1),
7259                       null, .35, 'easeIn', function(){
7260                          if(!visible){
7261                              if(visMode == El.DISPLAY){
7262                                  dom.style.display = "none";
7263                              }else{
7264                                  dom.style.visibility = "hidden";
7265                              }
7266                              Roo.get(dom).setOpacity(1);
7267                          }
7268                      });
7269             }
7270             return this;
7271         },
7272
7273         /**
7274          * Returns true if display is not "none"
7275          * @return {Boolean}
7276          */
7277         isDisplayed : function() {
7278             return this.getStyle("display") != "none";
7279         },
7280
7281         /**
7282          * Toggles the element's visibility or display, depending on visibility mode.
7283          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7284          * @return {Roo.Element} this
7285          */
7286         toggle : function(animate){
7287             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7288             return this;
7289         },
7290
7291         /**
7292          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7293          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7294          * @return {Roo.Element} this
7295          */
7296         setDisplayed : function(value) {
7297             if(typeof value == "boolean"){
7298                value = value ? this.originalDisplay : "none";
7299             }
7300             this.setStyle("display", value);
7301             return this;
7302         },
7303
7304         /**
7305          * Tries to focus the element. Any exceptions are caught and ignored.
7306          * @return {Roo.Element} this
7307          */
7308         focus : function() {
7309             try{
7310                 this.dom.focus();
7311             }catch(e){}
7312             return this;
7313         },
7314
7315         /**
7316          * Tries to blur the element. Any exceptions are caught and ignored.
7317          * @return {Roo.Element} this
7318          */
7319         blur : function() {
7320             try{
7321                 this.dom.blur();
7322             }catch(e){}
7323             return this;
7324         },
7325
7326         /**
7327          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7328          * @param {String/Array} className The CSS class to add, or an array of classes
7329          * @return {Roo.Element} this
7330          */
7331         addClass : function(className){
7332             if(className instanceof Array){
7333                 for(var i = 0, len = className.length; i < len; i++) {
7334                     this.addClass(className[i]);
7335                 }
7336             }else{
7337                 if(className && !this.hasClass(className)){
7338                     this.dom.className = this.dom.className + " " + className;
7339                 }
7340             }
7341             return this;
7342         },
7343
7344         /**
7345          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7346          * @param {String/Array} className The CSS class to add, or an array of classes
7347          * @return {Roo.Element} this
7348          */
7349         radioClass : function(className){
7350             var siblings = this.dom.parentNode.childNodes;
7351             for(var i = 0; i < siblings.length; i++) {
7352                 var s = siblings[i];
7353                 if(s.nodeType == 1){
7354                     Roo.get(s).removeClass(className);
7355                 }
7356             }
7357             this.addClass(className);
7358             return this;
7359         },
7360
7361         /**
7362          * Removes one or more CSS classes from the element.
7363          * @param {String/Array} className The CSS class to remove, or an array of classes
7364          * @return {Roo.Element} this
7365          */
7366         removeClass : function(className){
7367             if(!className || !this.dom.className){
7368                 return this;
7369             }
7370             if(className instanceof Array){
7371                 for(var i = 0, len = className.length; i < len; i++) {
7372                     this.removeClass(className[i]);
7373                 }
7374             }else{
7375                 if(this.hasClass(className)){
7376                     var re = this.classReCache[className];
7377                     if (!re) {
7378                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7379                        this.classReCache[className] = re;
7380                     }
7381                     this.dom.className =
7382                         this.dom.className.replace(re, " ");
7383                 }
7384             }
7385             return this;
7386         },
7387
7388         // private
7389         classReCache: {},
7390
7391         /**
7392          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7393          * @param {String} className The CSS class to toggle
7394          * @return {Roo.Element} this
7395          */
7396         toggleClass : function(className){
7397             if(this.hasClass(className)){
7398                 this.removeClass(className);
7399             }else{
7400                 this.addClass(className);
7401             }
7402             return this;
7403         },
7404
7405         /**
7406          * Checks if the specified CSS class exists on this element's DOM node.
7407          * @param {String} className The CSS class to check for
7408          * @return {Boolean} True if the class exists, else false
7409          */
7410         hasClass : function(className){
7411             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7412         },
7413
7414         /**
7415          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7416          * @param {String} oldClassName The CSS class to replace
7417          * @param {String} newClassName The replacement CSS class
7418          * @return {Roo.Element} this
7419          */
7420         replaceClass : function(oldClassName, newClassName){
7421             this.removeClass(oldClassName);
7422             this.addClass(newClassName);
7423             return this;
7424         },
7425
7426         /**
7427          * Returns an object with properties matching the styles requested.
7428          * For example, el.getStyles('color', 'font-size', 'width') might return
7429          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7430          * @param {String} style1 A style name
7431          * @param {String} style2 A style name
7432          * @param {String} etc.
7433          * @return {Object} The style object
7434          */
7435         getStyles : function(){
7436             var a = arguments, len = a.length, r = {};
7437             for(var i = 0; i < len; i++){
7438                 r[a[i]] = this.getStyle(a[i]);
7439             }
7440             return r;
7441         },
7442
7443         /**
7444          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7445          * @param {String} property The style property whose value is returned.
7446          * @return {String} The current value of the style property for this element.
7447          */
7448         getStyle : function(){
7449             return view && view.getComputedStyle ?
7450                 function(prop){
7451                     var el = this.dom, v, cs, camel;
7452                     if(prop == 'float'){
7453                         prop = "cssFloat";
7454                     }
7455                     if(el.style && (v = el.style[prop])){
7456                         return v;
7457                     }
7458                     if(cs = view.getComputedStyle(el, "")){
7459                         if(!(camel = propCache[prop])){
7460                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7461                         }
7462                         return cs[camel];
7463                     }
7464                     return null;
7465                 } :
7466                 function(prop){
7467                     var el = this.dom, v, cs, camel;
7468                     if(prop == 'opacity'){
7469                         if(typeof el.style.filter == 'string'){
7470                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7471                             if(m){
7472                                 var fv = parseFloat(m[1]);
7473                                 if(!isNaN(fv)){
7474                                     return fv ? fv / 100 : 0;
7475                                 }
7476                             }
7477                         }
7478                         return 1;
7479                     }else if(prop == 'float'){
7480                         prop = "styleFloat";
7481                     }
7482                     if(!(camel = propCache[prop])){
7483                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7484                     }
7485                     if(v = el.style[camel]){
7486                         return v;
7487                     }
7488                     if(cs = el.currentStyle){
7489                         return cs[camel];
7490                     }
7491                     return null;
7492                 };
7493         }(),
7494
7495         /**
7496          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7497          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7498          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7499          * @return {Roo.Element} this
7500          */
7501         setStyle : function(prop, value){
7502             if(typeof prop == "string"){
7503                 
7504                 if (prop == 'float') {
7505                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7506                     return this;
7507                 }
7508                 
7509                 var camel;
7510                 if(!(camel = propCache[prop])){
7511                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7512                 }
7513                 
7514                 if(camel == 'opacity') {
7515                     this.setOpacity(value);
7516                 }else{
7517                     this.dom.style[camel] = value;
7518                 }
7519             }else{
7520                 for(var style in prop){
7521                     if(typeof prop[style] != "function"){
7522                        this.setStyle(style, prop[style]);
7523                     }
7524                 }
7525             }
7526             return this;
7527         },
7528
7529         /**
7530          * More flexible version of {@link #setStyle} for setting style properties.
7531          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7532          * a function which returns such a specification.
7533          * @return {Roo.Element} this
7534          */
7535         applyStyles : function(style){
7536             Roo.DomHelper.applyStyles(this.dom, style);
7537             return this;
7538         },
7539
7540         /**
7541           * 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).
7542           * @return {Number} The X position of the element
7543           */
7544         getX : function(){
7545             return D.getX(this.dom);
7546         },
7547
7548         /**
7549           * 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).
7550           * @return {Number} The Y position of the element
7551           */
7552         getY : function(){
7553             return D.getY(this.dom);
7554         },
7555
7556         /**
7557           * 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).
7558           * @return {Array} The XY position of the element
7559           */
7560         getXY : function(){
7561             return D.getXY(this.dom);
7562         },
7563
7564         /**
7565          * 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).
7566          * @param {Number} The X 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         setX : function(x, animate){
7571             if(!animate || !A){
7572                 D.setX(this.dom, x);
7573             }else{
7574                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7575             }
7576             return this;
7577         },
7578
7579         /**
7580          * 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).
7581          * @param {Number} The Y position of the element
7582          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7583          * @return {Roo.Element} this
7584          */
7585         setY : function(y, animate){
7586             if(!animate || !A){
7587                 D.setY(this.dom, y);
7588             }else{
7589                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7590             }
7591             return this;
7592         },
7593
7594         /**
7595          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7596          * @param {String} left The left CSS property value
7597          * @return {Roo.Element} this
7598          */
7599         setLeft : function(left){
7600             this.setStyle("left", this.addUnits(left));
7601             return this;
7602         },
7603
7604         /**
7605          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7606          * @param {String} top The top CSS property value
7607          * @return {Roo.Element} this
7608          */
7609         setTop : function(top){
7610             this.setStyle("top", this.addUnits(top));
7611             return this;
7612         },
7613
7614         /**
7615          * Sets the element's CSS right style.
7616          * @param {String} right The right CSS property value
7617          * @return {Roo.Element} this
7618          */
7619         setRight : function(right){
7620             this.setStyle("right", this.addUnits(right));
7621             return this;
7622         },
7623
7624         /**
7625          * Sets the element's CSS bottom style.
7626          * @param {String} bottom The bottom CSS property value
7627          * @return {Roo.Element} this
7628          */
7629         setBottom : function(bottom){
7630             this.setStyle("bottom", this.addUnits(bottom));
7631             return this;
7632         },
7633
7634         /**
7635          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7636          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7637          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7638          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7639          * @return {Roo.Element} this
7640          */
7641         setXY : function(pos, animate){
7642             if(!animate || !A){
7643                 D.setXY(this.dom, pos);
7644             }else{
7645                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7646             }
7647             return this;
7648         },
7649
7650         /**
7651          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7652          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7653          * @param {Number} x X value for new position (coordinates are page-based)
7654          * @param {Number} y Y value for new position (coordinates are page-based)
7655          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7656          * @return {Roo.Element} this
7657          */
7658         setLocation : function(x, y, animate){
7659             this.setXY([x, y], this.preanim(arguments, 2));
7660             return this;
7661         },
7662
7663         /**
7664          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7665          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7666          * @param {Number} x X value for new position (coordinates are page-based)
7667          * @param {Number} y Y value for new position (coordinates are page-based)
7668          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7669          * @return {Roo.Element} this
7670          */
7671         moveTo : function(x, y, animate){
7672             this.setXY([x, y], this.preanim(arguments, 2));
7673             return this;
7674         },
7675
7676         /**
7677          * Returns the region of the given element.
7678          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7679          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7680          */
7681         getRegion : function(){
7682             return D.getRegion(this.dom);
7683         },
7684
7685         /**
7686          * Returns the offset height of the element
7687          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7688          * @return {Number} The element's height
7689          */
7690         getHeight : function(contentHeight){
7691             var h = this.dom.offsetHeight || 0;
7692             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7693         },
7694
7695         /**
7696          * Returns the offset width of the element
7697          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7698          * @return {Number} The element's width
7699          */
7700         getWidth : function(contentWidth){
7701             var w = this.dom.offsetWidth || 0;
7702             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7703         },
7704
7705         /**
7706          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7707          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7708          * if a height has not been set using CSS.
7709          * @return {Number}
7710          */
7711         getComputedHeight : function(){
7712             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7713             if(!h){
7714                 h = parseInt(this.getStyle('height'), 10) || 0;
7715                 if(!this.isBorderBox()){
7716                     h += this.getFrameWidth('tb');
7717                 }
7718             }
7719             return h;
7720         },
7721
7722         /**
7723          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7724          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7725          * if a width has not been set using CSS.
7726          * @return {Number}
7727          */
7728         getComputedWidth : function(){
7729             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7730             if(!w){
7731                 w = parseInt(this.getStyle('width'), 10) || 0;
7732                 if(!this.isBorderBox()){
7733                     w += this.getFrameWidth('lr');
7734                 }
7735             }
7736             return w;
7737         },
7738
7739         /**
7740          * Returns the size of the element.
7741          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7742          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7743          */
7744         getSize : function(contentSize){
7745             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7746         },
7747
7748         /**
7749          * Returns the width and height of the viewport.
7750          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7751          */
7752         getViewSize : function(){
7753             var d = this.dom, doc = document, aw = 0, ah = 0;
7754             if(d == doc || d == doc.body){
7755                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7756             }else{
7757                 return {
7758                     width : d.clientWidth,
7759                     height: d.clientHeight
7760                 };
7761             }
7762         },
7763
7764         /**
7765          * Returns the value of the "value" attribute
7766          * @param {Boolean} asNumber true to parse the value as a number
7767          * @return {String/Number}
7768          */
7769         getValue : function(asNumber){
7770             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7771         },
7772
7773         // private
7774         adjustWidth : function(width){
7775             if(typeof width == "number"){
7776                 if(this.autoBoxAdjust && !this.isBorderBox()){
7777                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7778                 }
7779                 if(width < 0){
7780                     width = 0;
7781                 }
7782             }
7783             return width;
7784         },
7785
7786         // private
7787         adjustHeight : function(height){
7788             if(typeof height == "number"){
7789                if(this.autoBoxAdjust && !this.isBorderBox()){
7790                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7791                }
7792                if(height < 0){
7793                    height = 0;
7794                }
7795             }
7796             return height;
7797         },
7798
7799         /**
7800          * Set the width of the element
7801          * @param {Number} width The new width
7802          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7803          * @return {Roo.Element} this
7804          */
7805         setWidth : function(width, animate){
7806             width = this.adjustWidth(width);
7807             if(!animate || !A){
7808                 this.dom.style.width = this.addUnits(width);
7809             }else{
7810                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7811             }
7812             return this;
7813         },
7814
7815         /**
7816          * Set the height of the element
7817          * @param {Number} height The new height
7818          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7819          * @return {Roo.Element} this
7820          */
7821          setHeight : function(height, animate){
7822             height = this.adjustHeight(height);
7823             if(!animate || !A){
7824                 this.dom.style.height = this.addUnits(height);
7825             }else{
7826                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7827             }
7828             return this;
7829         },
7830
7831         /**
7832          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7833          * @param {Number} width The new width
7834          * @param {Number} height The new height
7835          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7836          * @return {Roo.Element} this
7837          */
7838          setSize : function(width, height, animate){
7839             if(typeof width == "object"){ // in case of object from getSize()
7840                 height = width.height; width = width.width;
7841             }
7842             width = this.adjustWidth(width); height = this.adjustHeight(height);
7843             if(!animate || !A){
7844                 this.dom.style.width = this.addUnits(width);
7845                 this.dom.style.height = this.addUnits(height);
7846             }else{
7847                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7848             }
7849             return this;
7850         },
7851
7852         /**
7853          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7854          * @param {Number} x X value for new position (coordinates are page-based)
7855          * @param {Number} y Y value for new position (coordinates are page-based)
7856          * @param {Number} width The new width
7857          * @param {Number} height The new height
7858          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7859          * @return {Roo.Element} this
7860          */
7861         setBounds : function(x, y, width, height, animate){
7862             if(!animate || !A){
7863                 this.setSize(width, height);
7864                 this.setLocation(x, y);
7865             }else{
7866                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7867                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7868                               this.preanim(arguments, 4), 'motion');
7869             }
7870             return this;
7871         },
7872
7873         /**
7874          * 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.
7875          * @param {Roo.lib.Region} region The region to fill
7876          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7877          * @return {Roo.Element} this
7878          */
7879         setRegion : function(region, animate){
7880             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7881             return this;
7882         },
7883
7884         /**
7885          * Appends an event handler
7886          *
7887          * @param {String}   eventName     The type of event to append
7888          * @param {Function} fn        The method the event invokes
7889          * @param {Object} scope       (optional) The scope (this object) of the fn
7890          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7891          */
7892         addListener : function(eventName, fn, scope, options){
7893             if (this.dom) {
7894                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7895             }
7896         },
7897
7898         /**
7899          * Removes an event handler from this element
7900          * @param {String} eventName the type of event to remove
7901          * @param {Function} fn the method the event invokes
7902          * @return {Roo.Element} this
7903          */
7904         removeListener : function(eventName, fn){
7905             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7906             return this;
7907         },
7908
7909         /**
7910          * Removes all previous added listeners from this element
7911          * @return {Roo.Element} this
7912          */
7913         removeAllListeners : function(){
7914             E.purgeElement(this.dom);
7915             return this;
7916         },
7917
7918         relayEvent : function(eventName, observable){
7919             this.on(eventName, function(e){
7920                 observable.fireEvent(eventName, e);
7921             });
7922         },
7923
7924         /**
7925          * Set the opacity of the element
7926          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7927          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7928          * @return {Roo.Element} this
7929          */
7930          setOpacity : function(opacity, animate){
7931             if(!animate || !A){
7932                 var s = this.dom.style;
7933                 if(Roo.isIE){
7934                     s.zoom = 1;
7935                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7936                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7937                 }else{
7938                     s.opacity = opacity;
7939                 }
7940             }else{
7941                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7942             }
7943             return this;
7944         },
7945
7946         /**
7947          * Gets the left X coordinate
7948          * @param {Boolean} local True to get the local css position instead of page coordinate
7949          * @return {Number}
7950          */
7951         getLeft : function(local){
7952             if(!local){
7953                 return this.getX();
7954             }else{
7955                 return parseInt(this.getStyle("left"), 10) || 0;
7956             }
7957         },
7958
7959         /**
7960          * Gets the right X coordinate of the element (element X position + element width)
7961          * @param {Boolean} local True to get the local css position instead of page coordinate
7962          * @return {Number}
7963          */
7964         getRight : function(local){
7965             if(!local){
7966                 return this.getX() + this.getWidth();
7967             }else{
7968                 return (this.getLeft(true) + this.getWidth()) || 0;
7969             }
7970         },
7971
7972         /**
7973          * Gets the top Y coordinate
7974          * @param {Boolean} local True to get the local css position instead of page coordinate
7975          * @return {Number}
7976          */
7977         getTop : function(local) {
7978             if(!local){
7979                 return this.getY();
7980             }else{
7981                 return parseInt(this.getStyle("top"), 10) || 0;
7982             }
7983         },
7984
7985         /**
7986          * Gets the bottom Y coordinate of the element (element Y position + element height)
7987          * @param {Boolean} local True to get the local css position instead of page coordinate
7988          * @return {Number}
7989          */
7990         getBottom : function(local){
7991             if(!local){
7992                 return this.getY() + this.getHeight();
7993             }else{
7994                 return (this.getTop(true) + this.getHeight()) || 0;
7995             }
7996         },
7997
7998         /**
7999         * Initializes positioning on this element. If a desired position is not passed, it will make the
8000         * the element positioned relative IF it is not already positioned.
8001         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8002         * @param {Number} zIndex (optional) The zIndex to apply
8003         * @param {Number} x (optional) Set the page X position
8004         * @param {Number} y (optional) Set the page Y position
8005         */
8006         position : function(pos, zIndex, x, y){
8007             if(!pos){
8008                if(this.getStyle('position') == 'static'){
8009                    this.setStyle('position', 'relative');
8010                }
8011             }else{
8012                 this.setStyle("position", pos);
8013             }
8014             if(zIndex){
8015                 this.setStyle("z-index", zIndex);
8016             }
8017             if(x !== undefined && y !== undefined){
8018                 this.setXY([x, y]);
8019             }else if(x !== undefined){
8020                 this.setX(x);
8021             }else if(y !== undefined){
8022                 this.setY(y);
8023             }
8024         },
8025
8026         /**
8027         * Clear positioning back to the default when the document was loaded
8028         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8029         * @return {Roo.Element} this
8030          */
8031         clearPositioning : function(value){
8032             value = value ||'';
8033             this.setStyle({
8034                 "left": value,
8035                 "right": value,
8036                 "top": value,
8037                 "bottom": value,
8038                 "z-index": "",
8039                 "position" : "static"
8040             });
8041             return this;
8042         },
8043
8044         /**
8045         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8046         * snapshot before performing an update and then restoring the element.
8047         * @return {Object}
8048         */
8049         getPositioning : function(){
8050             var l = this.getStyle("left");
8051             var t = this.getStyle("top");
8052             return {
8053                 "position" : this.getStyle("position"),
8054                 "left" : l,
8055                 "right" : l ? "" : this.getStyle("right"),
8056                 "top" : t,
8057                 "bottom" : t ? "" : this.getStyle("bottom"),
8058                 "z-index" : this.getStyle("z-index")
8059             };
8060         },
8061
8062         /**
8063          * Gets the width of the border(s) for the specified side(s)
8064          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8065          * passing lr would get the border (l)eft width + the border (r)ight width.
8066          * @return {Number} The width of the sides passed added together
8067          */
8068         getBorderWidth : function(side){
8069             return this.addStyles(side, El.borders);
8070         },
8071
8072         /**
8073          * Gets the width of the padding(s) for the specified side(s)
8074          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8075          * passing lr would get the padding (l)eft + the padding (r)ight.
8076          * @return {Number} The padding of the sides passed added together
8077          */
8078         getPadding : function(side){
8079             return this.addStyles(side, El.paddings);
8080         },
8081
8082         /**
8083         * Set positioning with an object returned by getPositioning().
8084         * @param {Object} posCfg
8085         * @return {Roo.Element} this
8086          */
8087         setPositioning : function(pc){
8088             this.applyStyles(pc);
8089             if(pc.right == "auto"){
8090                 this.dom.style.right = "";
8091             }
8092             if(pc.bottom == "auto"){
8093                 this.dom.style.bottom = "";
8094             }
8095             return this;
8096         },
8097
8098         // private
8099         fixDisplay : function(){
8100             if(this.getStyle("display") == "none"){
8101                 this.setStyle("visibility", "hidden");
8102                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8103                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8104                     this.setStyle("display", "block");
8105                 }
8106             }
8107         },
8108
8109         /**
8110          * Quick set left and top adding default units
8111          * @param {String} left The left CSS property value
8112          * @param {String} top The top CSS property value
8113          * @return {Roo.Element} this
8114          */
8115          setLeftTop : function(left, top){
8116             this.dom.style.left = this.addUnits(left);
8117             this.dom.style.top = this.addUnits(top);
8118             return this;
8119         },
8120
8121         /**
8122          * Move this element relative to its current position.
8123          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8124          * @param {Number} distance How far to move the element in pixels
8125          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8126          * @return {Roo.Element} this
8127          */
8128          move : function(direction, distance, animate){
8129             var xy = this.getXY();
8130             direction = direction.toLowerCase();
8131             switch(direction){
8132                 case "l":
8133                 case "left":
8134                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8135                     break;
8136                case "r":
8137                case "right":
8138                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8139                     break;
8140                case "t":
8141                case "top":
8142                case "up":
8143                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8144                     break;
8145                case "b":
8146                case "bottom":
8147                case "down":
8148                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8149                     break;
8150             }
8151             return this;
8152         },
8153
8154         /**
8155          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8156          * @return {Roo.Element} this
8157          */
8158         clip : function(){
8159             if(!this.isClipped){
8160                this.isClipped = true;
8161                this.originalClip = {
8162                    "o": this.getStyle("overflow"),
8163                    "x": this.getStyle("overflow-x"),
8164                    "y": this.getStyle("overflow-y")
8165                };
8166                this.setStyle("overflow", "hidden");
8167                this.setStyle("overflow-x", "hidden");
8168                this.setStyle("overflow-y", "hidden");
8169             }
8170             return this;
8171         },
8172
8173         /**
8174          *  Return clipping (overflow) to original clipping before clip() was called
8175          * @return {Roo.Element} this
8176          */
8177         unclip : function(){
8178             if(this.isClipped){
8179                 this.isClipped = false;
8180                 var o = this.originalClip;
8181                 if(o.o){this.setStyle("overflow", o.o);}
8182                 if(o.x){this.setStyle("overflow-x", o.x);}
8183                 if(o.y){this.setStyle("overflow-y", o.y);}
8184             }
8185             return this;
8186         },
8187
8188
8189         /**
8190          * Gets the x,y coordinates specified by the anchor position on the element.
8191          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8192          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8193          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8194          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8195          * @return {Array} [x, y] An array containing the element's x and y coordinates
8196          */
8197         getAnchorXY : function(anchor, local, s){
8198             //Passing a different size is useful for pre-calculating anchors,
8199             //especially for anchored animations that change the el size.
8200
8201             var w, h, vp = false;
8202             if(!s){
8203                 var d = this.dom;
8204                 if(d == document.body || d == document){
8205                     vp = true;
8206                     w = D.getViewWidth(); h = D.getViewHeight();
8207                 }else{
8208                     w = this.getWidth(); h = this.getHeight();
8209                 }
8210             }else{
8211                 w = s.width;  h = s.height;
8212             }
8213             var x = 0, y = 0, r = Math.round;
8214             switch((anchor || "tl").toLowerCase()){
8215                 case "c":
8216                     x = r(w*.5);
8217                     y = r(h*.5);
8218                 break;
8219                 case "t":
8220                     x = r(w*.5);
8221                     y = 0;
8222                 break;
8223                 case "l":
8224                     x = 0;
8225                     y = r(h*.5);
8226                 break;
8227                 case "r":
8228                     x = w;
8229                     y = r(h*.5);
8230                 break;
8231                 case "b":
8232                     x = r(w*.5);
8233                     y = h;
8234                 break;
8235                 case "tl":
8236                     x = 0;
8237                     y = 0;
8238                 break;
8239                 case "bl":
8240                     x = 0;
8241                     y = h;
8242                 break;
8243                 case "br":
8244                     x = w;
8245                     y = h;
8246                 break;
8247                 case "tr":
8248                     x = w;
8249                     y = 0;
8250                 break;
8251             }
8252             if(local === true){
8253                 return [x, y];
8254             }
8255             if(vp){
8256                 var sc = this.getScroll();
8257                 return [x + sc.left, y + sc.top];
8258             }
8259             //Add the element's offset xy
8260             var o = this.getXY();
8261             return [x+o[0], y+o[1]];
8262         },
8263
8264         /**
8265          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8266          * supported position values.
8267          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8268          * @param {String} position The position to align to.
8269          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8270          * @return {Array} [x, y]
8271          */
8272         getAlignToXY : function(el, p, o){
8273             el = Roo.get(el);
8274             var d = this.dom;
8275             if(!el.dom){
8276                 throw "Element.alignTo with an element that doesn't exist";
8277             }
8278             var c = false; //constrain to viewport
8279             var p1 = "", p2 = "";
8280             o = o || [0,0];
8281
8282             if(!p){
8283                 p = "tl-bl";
8284             }else if(p == "?"){
8285                 p = "tl-bl?";
8286             }else if(p.indexOf("-") == -1){
8287                 p = "tl-" + p;
8288             }
8289             p = p.toLowerCase();
8290             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8291             if(!m){
8292                throw "Element.alignTo with an invalid alignment " + p;
8293             }
8294             p1 = m[1]; p2 = m[2]; c = !!m[3];
8295
8296             //Subtract the aligned el's internal xy from the target's offset xy
8297             //plus custom offset to get the aligned el's new offset xy
8298             var a1 = this.getAnchorXY(p1, true);
8299             var a2 = el.getAnchorXY(p2, false);
8300             var x = a2[0] - a1[0] + o[0];
8301             var y = a2[1] - a1[1] + o[1];
8302             if(c){
8303                 //constrain the aligned el to viewport if necessary
8304                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8305                 // 5px of margin for ie
8306                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8307
8308                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8309                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8310                 //otherwise swap the aligned el to the opposite border of the target.
8311                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8312                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8313                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8314                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8315
8316                var doc = document;
8317                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8318                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8319
8320                if((x+w) > dw + scrollX){
8321                     x = swapX ? r.left-w : dw+scrollX-w;
8322                 }
8323                if(x < scrollX){
8324                    x = swapX ? r.right : scrollX;
8325                }
8326                if((y+h) > dh + scrollY){
8327                     y = swapY ? r.top-h : dh+scrollY-h;
8328                 }
8329                if (y < scrollY){
8330                    y = swapY ? r.bottom : scrollY;
8331                }
8332             }
8333             return [x,y];
8334         },
8335
8336         // private
8337         getConstrainToXY : function(){
8338             var os = {top:0, left:0, bottom:0, right: 0};
8339
8340             return function(el, local, offsets, proposedXY){
8341                 el = Roo.get(el);
8342                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8343
8344                 var vw, vh, vx = 0, vy = 0;
8345                 if(el.dom == document.body || el.dom == document){
8346                     vw = Roo.lib.Dom.getViewWidth();
8347                     vh = Roo.lib.Dom.getViewHeight();
8348                 }else{
8349                     vw = el.dom.clientWidth;
8350                     vh = el.dom.clientHeight;
8351                     if(!local){
8352                         var vxy = el.getXY();
8353                         vx = vxy[0];
8354                         vy = vxy[1];
8355                     }
8356                 }
8357
8358                 var s = el.getScroll();
8359
8360                 vx += offsets.left + s.left;
8361                 vy += offsets.top + s.top;
8362
8363                 vw -= offsets.right;
8364                 vh -= offsets.bottom;
8365
8366                 var vr = vx+vw;
8367                 var vb = vy+vh;
8368
8369                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8370                 var x = xy[0], y = xy[1];
8371                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8372
8373                 // only move it if it needs it
8374                 var moved = false;
8375
8376                 // first validate right/bottom
8377                 if((x + w) > vr){
8378                     x = vr - w;
8379                     moved = true;
8380                 }
8381                 if((y + h) > vb){
8382                     y = vb - h;
8383                     moved = true;
8384                 }
8385                 // then make sure top/left isn't negative
8386                 if(x < vx){
8387                     x = vx;
8388                     moved = true;
8389                 }
8390                 if(y < vy){
8391                     y = vy;
8392                     moved = true;
8393                 }
8394                 return moved ? [x, y] : false;
8395             };
8396         }(),
8397
8398         // private
8399         adjustForConstraints : function(xy, parent, offsets){
8400             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8401         },
8402
8403         /**
8404          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8405          * document it aligns it to the viewport.
8406          * The position parameter is optional, and can be specified in any one of the following formats:
8407          * <ul>
8408          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8409          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8410          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8411          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8412          *   <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
8413          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8414          * </ul>
8415          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8416          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8417          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8418          * that specified in order to enforce the viewport constraints.
8419          * Following are all of the supported anchor positions:
8420     <pre>
8421     Value  Description
8422     -----  -----------------------------
8423     tl     The top left corner (default)
8424     t      The center of the top edge
8425     tr     The top right corner
8426     l      The center of the left edge
8427     c      In the center of the element
8428     r      The center of the right edge
8429     bl     The bottom left corner
8430     b      The center of the bottom edge
8431     br     The bottom right corner
8432     </pre>
8433     Example Usage:
8434     <pre><code>
8435     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8436     el.alignTo("other-el");
8437
8438     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8439     el.alignTo("other-el", "tr?");
8440
8441     // align the bottom right corner of el with the center left edge of other-el
8442     el.alignTo("other-el", "br-l?");
8443
8444     // align the center of el with the bottom left corner of other-el and
8445     // adjust the x position by -6 pixels (and the y position by 0)
8446     el.alignTo("other-el", "c-bl", [-6, 0]);
8447     </code></pre>
8448          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8449          * @param {String} position The position to align to.
8450          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8451          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8452          * @return {Roo.Element} this
8453          */
8454         alignTo : function(element, position, offsets, animate){
8455             var xy = this.getAlignToXY(element, position, offsets);
8456             this.setXY(xy, this.preanim(arguments, 3));
8457             return this;
8458         },
8459
8460         /**
8461          * Anchors an element to another element and realigns it when the window is resized.
8462          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8463          * @param {String} position The position to align to.
8464          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8465          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8466          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8467          * is a number, it is used as the buffer delay (defaults to 50ms).
8468          * @param {Function} callback The function to call after the animation finishes
8469          * @return {Roo.Element} this
8470          */
8471         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8472             var action = function(){
8473                 this.alignTo(el, alignment, offsets, animate);
8474                 Roo.callback(callback, this);
8475             };
8476             Roo.EventManager.onWindowResize(action, this);
8477             var tm = typeof monitorScroll;
8478             if(tm != 'undefined'){
8479                 Roo.EventManager.on(window, 'scroll', action, this,
8480                     {buffer: tm == 'number' ? monitorScroll : 50});
8481             }
8482             action.call(this); // align immediately
8483             return this;
8484         },
8485         /**
8486          * Clears any opacity settings from this element. Required in some cases for IE.
8487          * @return {Roo.Element} this
8488          */
8489         clearOpacity : function(){
8490             if (window.ActiveXObject) {
8491                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8492                     this.dom.style.filter = "";
8493                 }
8494             } else {
8495                 this.dom.style.opacity = "";
8496                 this.dom.style["-moz-opacity"] = "";
8497                 this.dom.style["-khtml-opacity"] = "";
8498             }
8499             return this;
8500         },
8501
8502         /**
8503          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8504          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8505          * @return {Roo.Element} this
8506          */
8507         hide : function(animate){
8508             this.setVisible(false, this.preanim(arguments, 0));
8509             return this;
8510         },
8511
8512         /**
8513         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8514         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8515          * @return {Roo.Element} this
8516          */
8517         show : function(animate){
8518             this.setVisible(true, this.preanim(arguments, 0));
8519             return this;
8520         },
8521
8522         /**
8523          * @private Test if size has a unit, otherwise appends the default
8524          */
8525         addUnits : function(size){
8526             return Roo.Element.addUnits(size, this.defaultUnit);
8527         },
8528
8529         /**
8530          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8531          * @return {Roo.Element} this
8532          */
8533         beginMeasure : function(){
8534             var el = this.dom;
8535             if(el.offsetWidth || el.offsetHeight){
8536                 return this; // offsets work already
8537             }
8538             var changed = [];
8539             var p = this.dom, b = document.body; // start with this element
8540             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8541                 var pe = Roo.get(p);
8542                 if(pe.getStyle('display') == 'none'){
8543                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8544                     p.style.visibility = "hidden";
8545                     p.style.display = "block";
8546                 }
8547                 p = p.parentNode;
8548             }
8549             this._measureChanged = changed;
8550             return this;
8551
8552         },
8553
8554         /**
8555          * Restores displays to before beginMeasure was called
8556          * @return {Roo.Element} this
8557          */
8558         endMeasure : function(){
8559             var changed = this._measureChanged;
8560             if(changed){
8561                 for(var i = 0, len = changed.length; i < len; i++) {
8562                     var r = changed[i];
8563                     r.el.style.visibility = r.visibility;
8564                     r.el.style.display = "none";
8565                 }
8566                 this._measureChanged = null;
8567             }
8568             return this;
8569         },
8570
8571         /**
8572         * Update the innerHTML of this element, optionally searching for and processing scripts
8573         * @param {String} html The new HTML
8574         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8575         * @param {Function} callback For async script loading you can be noticed when the update completes
8576         * @return {Roo.Element} this
8577          */
8578         update : function(html, loadScripts, callback){
8579             if(typeof html == "undefined"){
8580                 html = "";
8581             }
8582             if(loadScripts !== true){
8583                 this.dom.innerHTML = html;
8584                 if(typeof callback == "function"){
8585                     callback();
8586                 }
8587                 return this;
8588             }
8589             var id = Roo.id();
8590             var dom = this.dom;
8591
8592             html += '<span id="' + id + '"></span>';
8593
8594             E.onAvailable(id, function(){
8595                 var hd = document.getElementsByTagName("head")[0];
8596                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8597                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8598                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8599
8600                 var match;
8601                 while(match = re.exec(html)){
8602                     var attrs = match[1];
8603                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8604                     if(srcMatch && srcMatch[2]){
8605                        var s = document.createElement("script");
8606                        s.src = srcMatch[2];
8607                        var typeMatch = attrs.match(typeRe);
8608                        if(typeMatch && typeMatch[2]){
8609                            s.type = typeMatch[2];
8610                        }
8611                        hd.appendChild(s);
8612                     }else if(match[2] && match[2].length > 0){
8613                         if(window.execScript) {
8614                            window.execScript(match[2]);
8615                         } else {
8616                             /**
8617                              * eval:var:id
8618                              * eval:var:dom
8619                              * eval:var:html
8620                              * 
8621                              */
8622                            window.eval(match[2]);
8623                         }
8624                     }
8625                 }
8626                 var el = document.getElementById(id);
8627                 if(el){el.parentNode.removeChild(el);}
8628                 if(typeof callback == "function"){
8629                     callback();
8630                 }
8631             });
8632             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8633             return this;
8634         },
8635
8636         /**
8637          * Direct access to the UpdateManager update() method (takes the same parameters).
8638          * @param {String/Function} url The url for this request or a function to call to get the url
8639          * @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}
8640          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8641          * @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.
8642          * @return {Roo.Element} this
8643          */
8644         load : function(){
8645             var um = this.getUpdateManager();
8646             um.update.apply(um, arguments);
8647             return this;
8648         },
8649
8650         /**
8651         * Gets this element's UpdateManager
8652         * @return {Roo.UpdateManager} The UpdateManager
8653         */
8654         getUpdateManager : function(){
8655             if(!this.updateManager){
8656                 this.updateManager = new Roo.UpdateManager(this);
8657             }
8658             return this.updateManager;
8659         },
8660
8661         /**
8662          * Disables text selection for this element (normalized across browsers)
8663          * @return {Roo.Element} this
8664          */
8665         unselectable : function(){
8666             this.dom.unselectable = "on";
8667             this.swallowEvent("selectstart", true);
8668             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8669             this.addClass("x-unselectable");
8670             return this;
8671         },
8672
8673         /**
8674         * Calculates the x, y to center this element on the screen
8675         * @return {Array} The x, y values [x, y]
8676         */
8677         getCenterXY : function(){
8678             return this.getAlignToXY(document, 'c-c');
8679         },
8680
8681         /**
8682         * Centers the Element in either the viewport, or another Element.
8683         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8684         */
8685         center : function(centerIn){
8686             this.alignTo(centerIn || document, 'c-c');
8687             return this;
8688         },
8689
8690         /**
8691          * Tests various css rules/browsers to determine if this element uses a border box
8692          * @return {Boolean}
8693          */
8694         isBorderBox : function(){
8695             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8696         },
8697
8698         /**
8699          * Return a box {x, y, width, height} that can be used to set another elements
8700          * size/location to match this element.
8701          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8702          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8703          * @return {Object} box An object in the format {x, y, width, height}
8704          */
8705         getBox : function(contentBox, local){
8706             var xy;
8707             if(!local){
8708                 xy = this.getXY();
8709             }else{
8710                 var left = parseInt(this.getStyle("left"), 10) || 0;
8711                 var top = parseInt(this.getStyle("top"), 10) || 0;
8712                 xy = [left, top];
8713             }
8714             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8715             if(!contentBox){
8716                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8717             }else{
8718                 var l = this.getBorderWidth("l")+this.getPadding("l");
8719                 var r = this.getBorderWidth("r")+this.getPadding("r");
8720                 var t = this.getBorderWidth("t")+this.getPadding("t");
8721                 var b = this.getBorderWidth("b")+this.getPadding("b");
8722                 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)};
8723             }
8724             bx.right = bx.x + bx.width;
8725             bx.bottom = bx.y + bx.height;
8726             return bx;
8727         },
8728
8729         /**
8730          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8731          for more information about the sides.
8732          * @param {String} sides
8733          * @return {Number}
8734          */
8735         getFrameWidth : function(sides, onlyContentBox){
8736             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8737         },
8738
8739         /**
8740          * 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.
8741          * @param {Object} box The box to fill {x, y, width, height}
8742          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8743          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8744          * @return {Roo.Element} this
8745          */
8746         setBox : function(box, adjust, animate){
8747             var w = box.width, h = box.height;
8748             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8749                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8750                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8751             }
8752             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8753             return this;
8754         },
8755
8756         /**
8757          * Forces the browser to repaint this element
8758          * @return {Roo.Element} this
8759          */
8760          repaint : function(){
8761             var dom = this.dom;
8762             this.addClass("x-repaint");
8763             setTimeout(function(){
8764                 Roo.get(dom).removeClass("x-repaint");
8765             }, 1);
8766             return this;
8767         },
8768
8769         /**
8770          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8771          * then it returns the calculated width of the sides (see getPadding)
8772          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8773          * @return {Object/Number}
8774          */
8775         getMargins : function(side){
8776             if(!side){
8777                 return {
8778                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8779                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8780                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8781                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8782                 };
8783             }else{
8784                 return this.addStyles(side, El.margins);
8785              }
8786         },
8787
8788         // private
8789         addStyles : function(sides, styles){
8790             var val = 0, v, w;
8791             for(var i = 0, len = sides.length; i < len; i++){
8792                 v = this.getStyle(styles[sides.charAt(i)]);
8793                 if(v){
8794                      w = parseInt(v, 10);
8795                      if(w){ val += w; }
8796                 }
8797             }
8798             return val;
8799         },
8800
8801         /**
8802          * Creates a proxy element of this element
8803          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8804          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8805          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8806          * @return {Roo.Element} The new proxy element
8807          */
8808         createProxy : function(config, renderTo, matchBox){
8809             if(renderTo){
8810                 renderTo = Roo.getDom(renderTo);
8811             }else{
8812                 renderTo = document.body;
8813             }
8814             config = typeof config == "object" ?
8815                 config : {tag : "div", cls: config};
8816             var proxy = Roo.DomHelper.append(renderTo, config, true);
8817             if(matchBox){
8818                proxy.setBox(this.getBox());
8819             }
8820             return proxy;
8821         },
8822
8823         /**
8824          * Puts a mask over this element to disable user interaction. Requires core.css.
8825          * This method can only be applied to elements which accept child nodes.
8826          * @param {String} msg (optional) A message to display in the mask
8827          * @param {String} msgCls (optional) A css class to apply to the msg element
8828          * @return {Element} The mask  element
8829          */
8830         mask : function(msg, msgCls){
8831             if(this.getStyle("position") == "static"){
8832                 this.setStyle("position", "relative");
8833             }
8834             if(!this._mask){
8835                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8836             }
8837             this.addClass("x-masked");
8838             this._mask.setDisplayed(true);
8839             if(typeof msg == 'string'){
8840                 if(!this._maskMsg){
8841                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8842                 }
8843                 var mm = this._maskMsg;
8844                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8845                 mm.dom.firstChild.innerHTML = msg;
8846                 mm.setDisplayed(true);
8847                 mm.center(this);
8848             }
8849             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8850                 this._mask.setHeight(this.getHeight());
8851             }
8852             return this._mask;
8853         },
8854
8855         /**
8856          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8857          * it is cached for reuse.
8858          */
8859         unmask : function(removeEl){
8860             if(this._mask){
8861                 if(removeEl === true){
8862                     this._mask.remove();
8863                     delete this._mask;
8864                     if(this._maskMsg){
8865                         this._maskMsg.remove();
8866                         delete this._maskMsg;
8867                     }
8868                 }else{
8869                     this._mask.setDisplayed(false);
8870                     if(this._maskMsg){
8871                         this._maskMsg.setDisplayed(false);
8872                     }
8873                 }
8874             }
8875             this.removeClass("x-masked");
8876         },
8877
8878         /**
8879          * Returns true if this element is masked
8880          * @return {Boolean}
8881          */
8882         isMasked : function(){
8883             return this._mask && this._mask.isVisible();
8884         },
8885
8886         /**
8887          * Creates an iframe shim for this element to keep selects and other windowed objects from
8888          * showing through.
8889          * @return {Roo.Element} The new shim element
8890          */
8891         createShim : function(){
8892             var el = document.createElement('iframe');
8893             el.frameBorder = 'no';
8894             el.className = 'roo-shim';
8895             if(Roo.isIE && Roo.isSecure){
8896                 el.src = Roo.SSL_SECURE_URL;
8897             }
8898             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8899             shim.autoBoxAdjust = false;
8900             return shim;
8901         },
8902
8903         /**
8904          * Removes this element from the DOM and deletes it from the cache
8905          */
8906         remove : function(){
8907             if(this.dom.parentNode){
8908                 this.dom.parentNode.removeChild(this.dom);
8909             }
8910             delete El.cache[this.dom.id];
8911         },
8912
8913         /**
8914          * Sets up event handlers to add and remove a css class when the mouse is over this element
8915          * @param {String} className
8916          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8917          * mouseout events for children elements
8918          * @return {Roo.Element} this
8919          */
8920         addClassOnOver : function(className, preventFlicker){
8921             this.on("mouseover", function(){
8922                 Roo.fly(this, '_internal').addClass(className);
8923             }, this.dom);
8924             var removeFn = function(e){
8925                 if(preventFlicker !== true || !e.within(this, true)){
8926                     Roo.fly(this, '_internal').removeClass(className);
8927                 }
8928             };
8929             this.on("mouseout", removeFn, this.dom);
8930             return this;
8931         },
8932
8933         /**
8934          * Sets up event handlers to add and remove a css class when this element has the focus
8935          * @param {String} className
8936          * @return {Roo.Element} this
8937          */
8938         addClassOnFocus : function(className){
8939             this.on("focus", function(){
8940                 Roo.fly(this, '_internal').addClass(className);
8941             }, this.dom);
8942             this.on("blur", function(){
8943                 Roo.fly(this, '_internal').removeClass(className);
8944             }, this.dom);
8945             return this;
8946         },
8947         /**
8948          * 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)
8949          * @param {String} className
8950          * @return {Roo.Element} this
8951          */
8952         addClassOnClick : function(className){
8953             var dom = this.dom;
8954             this.on("mousedown", function(){
8955                 Roo.fly(dom, '_internal').addClass(className);
8956                 var d = Roo.get(document);
8957                 var fn = function(){
8958                     Roo.fly(dom, '_internal').removeClass(className);
8959                     d.removeListener("mouseup", fn);
8960                 };
8961                 d.on("mouseup", fn);
8962             });
8963             return this;
8964         },
8965
8966         /**
8967          * Stops the specified event from bubbling and optionally prevents the default action
8968          * @param {String} eventName
8969          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8970          * @return {Roo.Element} this
8971          */
8972         swallowEvent : function(eventName, preventDefault){
8973             var fn = function(e){
8974                 e.stopPropagation();
8975                 if(preventDefault){
8976                     e.preventDefault();
8977                 }
8978             };
8979             if(eventName instanceof Array){
8980                 for(var i = 0, len = eventName.length; i < len; i++){
8981                      this.on(eventName[i], fn);
8982                 }
8983                 return this;
8984             }
8985             this.on(eventName, fn);
8986             return this;
8987         },
8988
8989         /**
8990          * @private
8991          */
8992       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8993
8994         /**
8995          * Sizes this element to its parent element's dimensions performing
8996          * neccessary box adjustments.
8997          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8998          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8999          * @return {Roo.Element} this
9000          */
9001         fitToParent : function(monitorResize, targetParent) {
9002           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9003           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9004           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9005             return;
9006           }
9007           var p = Roo.get(targetParent || this.dom.parentNode);
9008           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9009           if (monitorResize === true) {
9010             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9011             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9012           }
9013           return this;
9014         },
9015
9016         /**
9017          * Gets the next sibling, skipping text nodes
9018          * @return {HTMLElement} The next sibling or null
9019          */
9020         getNextSibling : function(){
9021             var n = this.dom.nextSibling;
9022             while(n && n.nodeType != 1){
9023                 n = n.nextSibling;
9024             }
9025             return n;
9026         },
9027
9028         /**
9029          * Gets the previous sibling, skipping text nodes
9030          * @return {HTMLElement} The previous sibling or null
9031          */
9032         getPrevSibling : function(){
9033             var n = this.dom.previousSibling;
9034             while(n && n.nodeType != 1){
9035                 n = n.previousSibling;
9036             }
9037             return n;
9038         },
9039
9040
9041         /**
9042          * Appends the passed element(s) to this element
9043          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9044          * @return {Roo.Element} this
9045          */
9046         appendChild: function(el){
9047             el = Roo.get(el);
9048             el.appendTo(this);
9049             return this;
9050         },
9051
9052         /**
9053          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9054          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9055          * automatically generated with the specified attributes.
9056          * @param {HTMLElement} insertBefore (optional) a child element of this element
9057          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9058          * @return {Roo.Element} The new child element
9059          */
9060         createChild: function(config, insertBefore, returnDom){
9061             config = config || {tag:'div'};
9062             if(insertBefore){
9063                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9064             }
9065             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9066         },
9067
9068         /**
9069          * Appends this element to the passed element
9070          * @param {String/HTMLElement/Element} el The new parent element
9071          * @return {Roo.Element} this
9072          */
9073         appendTo: function(el){
9074             el = Roo.getDom(el);
9075             el.appendChild(this.dom);
9076             return this;
9077         },
9078
9079         /**
9080          * Inserts this element before the passed element in the DOM
9081          * @param {String/HTMLElement/Element} el The element to insert before
9082          * @return {Roo.Element} this
9083          */
9084         insertBefore: function(el){
9085             el = Roo.getDom(el);
9086             el.parentNode.insertBefore(this.dom, el);
9087             return this;
9088         },
9089
9090         /**
9091          * Inserts this element after the passed element in the DOM
9092          * @param {String/HTMLElement/Element} el The element to insert after
9093          * @return {Roo.Element} this
9094          */
9095         insertAfter: function(el){
9096             el = Roo.getDom(el);
9097             el.parentNode.insertBefore(this.dom, el.nextSibling);
9098             return this;
9099         },
9100
9101         /**
9102          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9103          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9104          * @return {Roo.Element} The new child
9105          */
9106         insertFirst: function(el, returnDom){
9107             el = el || {};
9108             if(typeof el == 'object' && !el.nodeType){ // dh config
9109                 return this.createChild(el, this.dom.firstChild, returnDom);
9110             }else{
9111                 el = Roo.getDom(el);
9112                 this.dom.insertBefore(el, this.dom.firstChild);
9113                 return !returnDom ? Roo.get(el) : el;
9114             }
9115         },
9116
9117         /**
9118          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9119          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9120          * @param {String} where (optional) 'before' or 'after' defaults to before
9121          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9122          * @return {Roo.Element} the inserted Element
9123          */
9124         insertSibling: function(el, where, returnDom){
9125             where = where ? where.toLowerCase() : 'before';
9126             el = el || {};
9127             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9128
9129             if(typeof el == 'object' && !el.nodeType){ // dh config
9130                 if(where == 'after' && !this.dom.nextSibling){
9131                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9132                 }else{
9133                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9134                 }
9135
9136             }else{
9137                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9138                             where == 'before' ? this.dom : this.dom.nextSibling);
9139                 if(!returnDom){
9140                     rt = Roo.get(rt);
9141                 }
9142             }
9143             return rt;
9144         },
9145
9146         /**
9147          * Creates and wraps this element with another element
9148          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9149          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9150          * @return {HTMLElement/Element} The newly created wrapper element
9151          */
9152         wrap: function(config, returnDom){
9153             if(!config){
9154                 config = {tag: "div"};
9155             }
9156             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9157             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9158             return newEl;
9159         },
9160
9161         /**
9162          * Replaces the passed element with this element
9163          * @param {String/HTMLElement/Element} el The element to replace
9164          * @return {Roo.Element} this
9165          */
9166         replace: function(el){
9167             el = Roo.get(el);
9168             this.insertBefore(el);
9169             el.remove();
9170             return this;
9171         },
9172
9173         /**
9174          * Inserts an html fragment into this element
9175          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9176          * @param {String} html The HTML fragment
9177          * @param {Boolean} returnEl True to return an Roo.Element
9178          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9179          */
9180         insertHtml : function(where, html, returnEl){
9181             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9182             return returnEl ? Roo.get(el) : el;
9183         },
9184
9185         /**
9186          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9187          * @param {Object} o The object with the attributes
9188          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9189          * @return {Roo.Element} this
9190          */
9191         set : function(o, useSet){
9192             var el = this.dom;
9193             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9194             for(var attr in o){
9195                 if(attr == "style" || typeof o[attr] == "function") continue;
9196                 if(attr=="cls"){
9197                     el.className = o["cls"];
9198                 }else{
9199                     if(useSet) el.setAttribute(attr, o[attr]);
9200                     else el[attr] = o[attr];
9201                 }
9202             }
9203             if(o.style){
9204                 Roo.DomHelper.applyStyles(el, o.style);
9205             }
9206             return this;
9207         },
9208
9209         /**
9210          * Convenience method for constructing a KeyMap
9211          * @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:
9212          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9213          * @param {Function} fn The function to call
9214          * @param {Object} scope (optional) The scope of the function
9215          * @return {Roo.KeyMap} The KeyMap created
9216          */
9217         addKeyListener : function(key, fn, scope){
9218             var config;
9219             if(typeof key != "object" || key instanceof Array){
9220                 config = {
9221                     key: key,
9222                     fn: fn,
9223                     scope: scope
9224                 };
9225             }else{
9226                 config = {
9227                     key : key.key,
9228                     shift : key.shift,
9229                     ctrl : key.ctrl,
9230                     alt : key.alt,
9231                     fn: fn,
9232                     scope: scope
9233                 };
9234             }
9235             return new Roo.KeyMap(this, config);
9236         },
9237
9238         /**
9239          * Creates a KeyMap for this element
9240          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9241          * @return {Roo.KeyMap} The KeyMap created
9242          */
9243         addKeyMap : function(config){
9244             return new Roo.KeyMap(this, config);
9245         },
9246
9247         /**
9248          * Returns true if this element is scrollable.
9249          * @return {Boolean}
9250          */
9251          isScrollable : function(){
9252             var dom = this.dom;
9253             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9254         },
9255
9256         /**
9257          * 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().
9258          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9259          * @param {Number} value The new scroll value
9260          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9261          * @return {Element} this
9262          */
9263
9264         scrollTo : function(side, value, animate){
9265             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9266             if(!animate || !A){
9267                 this.dom[prop] = value;
9268             }else{
9269                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9270                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9271             }
9272             return this;
9273         },
9274
9275         /**
9276          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9277          * within this element's scrollable range.
9278          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9279          * @param {Number} distance How far to scroll the element in pixels
9280          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9281          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9282          * was scrolled as far as it could go.
9283          */
9284          scroll : function(direction, distance, animate){
9285              if(!this.isScrollable()){
9286                  return;
9287              }
9288              var el = this.dom;
9289              var l = el.scrollLeft, t = el.scrollTop;
9290              var w = el.scrollWidth, h = el.scrollHeight;
9291              var cw = el.clientWidth, ch = el.clientHeight;
9292              direction = direction.toLowerCase();
9293              var scrolled = false;
9294              var a = this.preanim(arguments, 2);
9295              switch(direction){
9296                  case "l":
9297                  case "left":
9298                      if(w - l > cw){
9299                          var v = Math.min(l + distance, w-cw);
9300                          this.scrollTo("left", v, a);
9301                          scrolled = true;
9302                      }
9303                      break;
9304                 case "r":
9305                 case "right":
9306                      if(l > 0){
9307                          var v = Math.max(l - distance, 0);
9308                          this.scrollTo("left", v, a);
9309                          scrolled = true;
9310                      }
9311                      break;
9312                 case "t":
9313                 case "top":
9314                 case "up":
9315                      if(t > 0){
9316                          var v = Math.max(t - distance, 0);
9317                          this.scrollTo("top", v, a);
9318                          scrolled = true;
9319                      }
9320                      break;
9321                 case "b":
9322                 case "bottom":
9323                 case "down":
9324                      if(h - t > ch){
9325                          var v = Math.min(t + distance, h-ch);
9326                          this.scrollTo("top", v, a);
9327                          scrolled = true;
9328                      }
9329                      break;
9330              }
9331              return scrolled;
9332         },
9333
9334         /**
9335          * Translates the passed page coordinates into left/top css values for this element
9336          * @param {Number/Array} x The page x or an array containing [x, y]
9337          * @param {Number} y The page y
9338          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9339          */
9340         translatePoints : function(x, y){
9341             if(typeof x == 'object' || x instanceof Array){
9342                 y = x[1]; x = x[0];
9343             }
9344             var p = this.getStyle('position');
9345             var o = this.getXY();
9346
9347             var l = parseInt(this.getStyle('left'), 10);
9348             var t = parseInt(this.getStyle('top'), 10);
9349
9350             if(isNaN(l)){
9351                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9352             }
9353             if(isNaN(t)){
9354                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9355             }
9356
9357             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9358         },
9359
9360         /**
9361          * Returns the current scroll position of the element.
9362          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9363          */
9364         getScroll : function(){
9365             var d = this.dom, doc = document;
9366             if(d == doc || d == doc.body){
9367                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9368                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9369                 return {left: l, top: t};
9370             }else{
9371                 return {left: d.scrollLeft, top: d.scrollTop};
9372             }
9373         },
9374
9375         /**
9376          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9377          * are convert to standard 6 digit hex color.
9378          * @param {String} attr The css attribute
9379          * @param {String} defaultValue The default value to use when a valid color isn't found
9380          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9381          * YUI color anims.
9382          */
9383         getColor : function(attr, defaultValue, prefix){
9384             var v = this.getStyle(attr);
9385             if(!v || v == "transparent" || v == "inherit") {
9386                 return defaultValue;
9387             }
9388             var color = typeof prefix == "undefined" ? "#" : prefix;
9389             if(v.substr(0, 4) == "rgb("){
9390                 var rvs = v.slice(4, v.length -1).split(",");
9391                 for(var i = 0; i < 3; i++){
9392                     var h = parseInt(rvs[i]).toString(16);
9393                     if(h < 16){
9394                         h = "0" + h;
9395                     }
9396                     color += h;
9397                 }
9398             } else {
9399                 if(v.substr(0, 1) == "#"){
9400                     if(v.length == 4) {
9401                         for(var i = 1; i < 4; i++){
9402                             var c = v.charAt(i);
9403                             color +=  c + c;
9404                         }
9405                     }else if(v.length == 7){
9406                         color += v.substr(1);
9407                     }
9408                 }
9409             }
9410             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9411         },
9412
9413         /**
9414          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9415          * gradient background, rounded corners and a 4-way shadow.
9416          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9417          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9418          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9419          * @return {Roo.Element} this
9420          */
9421         boxWrap : function(cls){
9422             cls = cls || 'x-box';
9423             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9424             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9425             return el;
9426         },
9427
9428         /**
9429          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9430          * @param {String} namespace The namespace in which to look for the attribute
9431          * @param {String} name The attribute name
9432          * @return {String} The attribute value
9433          */
9434         getAttributeNS : Roo.isIE ? function(ns, name){
9435             var d = this.dom;
9436             var type = typeof d[ns+":"+name];
9437             if(type != 'undefined' && type != 'unknown'){
9438                 return d[ns+":"+name];
9439             }
9440             return d[name];
9441         } : function(ns, name){
9442             var d = this.dom;
9443             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9444         }
9445     };
9446
9447     var ep = El.prototype;
9448
9449     /**
9450      * Appends an event handler (Shorthand for addListener)
9451      * @param {String}   eventName     The type of event to append
9452      * @param {Function} fn        The method the event invokes
9453      * @param {Object} scope       (optional) The scope (this object) of the fn
9454      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9455      * @method
9456      */
9457     ep.on = ep.addListener;
9458         // backwards compat
9459     ep.mon = ep.addListener;
9460
9461     /**
9462      * Removes an event handler from this element (shorthand for removeListener)
9463      * @param {String} eventName the type of event to remove
9464      * @param {Function} fn the method the event invokes
9465      * @return {Roo.Element} this
9466      * @method
9467      */
9468     ep.un = ep.removeListener;
9469
9470     /**
9471      * true to automatically adjust width and height settings for box-model issues (default to true)
9472      */
9473     ep.autoBoxAdjust = true;
9474
9475     // private
9476     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9477
9478     // private
9479     El.addUnits = function(v, defaultUnit){
9480         if(v === "" || v == "auto"){
9481             return v;
9482         }
9483         if(v === undefined){
9484             return '';
9485         }
9486         if(typeof v == "number" || !El.unitPattern.test(v)){
9487             return v + (defaultUnit || 'px');
9488         }
9489         return v;
9490     };
9491
9492     // special markup used throughout Roo when box wrapping elements
9493     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>';
9494     /**
9495      * Visibility mode constant - Use visibility to hide element
9496      * @static
9497      * @type Number
9498      */
9499     El.VISIBILITY = 1;
9500     /**
9501      * Visibility mode constant - Use display to hide element
9502      * @static
9503      * @type Number
9504      */
9505     El.DISPLAY = 2;
9506
9507     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9508     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9509     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9510
9511
9512
9513     /**
9514      * @private
9515      */
9516     El.cache = {};
9517
9518     var docEl;
9519
9520     /**
9521      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9522      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9523      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9524      * @return {Element} The Element object
9525      * @static
9526      */
9527     El.get = function(el){
9528         var ex, elm, id;
9529         if(!el){ return null; }
9530         if(typeof el == "string"){ // element id
9531             if(!(elm = document.getElementById(el))){
9532                 return null;
9533             }
9534             if(ex = El.cache[el]){
9535                 ex.dom = elm;
9536             }else{
9537                 ex = El.cache[el] = new El(elm);
9538             }
9539             return ex;
9540         }else if(el.tagName){ // dom element
9541             if(!(id = el.id)){
9542                 id = Roo.id(el);
9543             }
9544             if(ex = El.cache[id]){
9545                 ex.dom = el;
9546             }else{
9547                 ex = El.cache[id] = new El(el);
9548             }
9549             return ex;
9550         }else if(el instanceof El){
9551             if(el != docEl){
9552                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9553                                                               // catch case where it hasn't been appended
9554                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9555             }
9556             return el;
9557         }else if(el.isComposite){
9558             return el;
9559         }else if(el instanceof Array){
9560             return El.select(el);
9561         }else if(el == document){
9562             // create a bogus element object representing the document object
9563             if(!docEl){
9564                 var f = function(){};
9565                 f.prototype = El.prototype;
9566                 docEl = new f();
9567                 docEl.dom = document;
9568             }
9569             return docEl;
9570         }
9571         return null;
9572     };
9573
9574     // private
9575     El.uncache = function(el){
9576         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9577             if(a[i]){
9578                 delete El.cache[a[i].id || a[i]];
9579             }
9580         }
9581     };
9582
9583     // private
9584     // Garbage collection - uncache elements/purge listeners on orphaned elements
9585     // so we don't hold a reference and cause the browser to retain them
9586     El.garbageCollect = function(){
9587         if(!Roo.enableGarbageCollector){
9588             clearInterval(El.collectorThread);
9589             return;
9590         }
9591         for(var eid in El.cache){
9592             var el = El.cache[eid], d = el.dom;
9593             // -------------------------------------------------------
9594             // Determining what is garbage:
9595             // -------------------------------------------------------
9596             // !d
9597             // dom node is null, definitely garbage
9598             // -------------------------------------------------------
9599             // !d.parentNode
9600             // no parentNode == direct orphan, definitely garbage
9601             // -------------------------------------------------------
9602             // !d.offsetParent && !document.getElementById(eid)
9603             // display none elements have no offsetParent so we will
9604             // also try to look it up by it's id. However, check
9605             // offsetParent first so we don't do unneeded lookups.
9606             // This enables collection of elements that are not orphans
9607             // directly, but somewhere up the line they have an orphan
9608             // parent.
9609             // -------------------------------------------------------
9610             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9611                 delete El.cache[eid];
9612                 if(d && Roo.enableListenerCollection){
9613                     E.purgeElement(d);
9614                 }
9615             }
9616         }
9617     }
9618     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9619
9620
9621     // dom is optional
9622     El.Flyweight = function(dom){
9623         this.dom = dom;
9624     };
9625     El.Flyweight.prototype = El.prototype;
9626
9627     El._flyweights = {};
9628     /**
9629      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9630      * the dom node can be overwritten by other code.
9631      * @param {String/HTMLElement} el The dom node or id
9632      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9633      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9634      * @static
9635      * @return {Element} The shared Element object
9636      */
9637     El.fly = function(el, named){
9638         named = named || '_global';
9639         el = Roo.getDom(el);
9640         if(!el){
9641             return null;
9642         }
9643         if(!El._flyweights[named]){
9644             El._flyweights[named] = new El.Flyweight();
9645         }
9646         El._flyweights[named].dom = el;
9647         return El._flyweights[named];
9648     };
9649
9650     /**
9651      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9652      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9653      * Shorthand of {@link Roo.Element#get}
9654      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9655      * @return {Element} The Element object
9656      * @member Roo
9657      * @method get
9658      */
9659     Roo.get = El.get;
9660     /**
9661      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9662      * the dom node can be overwritten by other code.
9663      * Shorthand of {@link Roo.Element#fly}
9664      * @param {String/HTMLElement} el The dom node or id
9665      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9666      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9667      * @static
9668      * @return {Element} The shared Element object
9669      * @member Roo
9670      * @method fly
9671      */
9672     Roo.fly = El.fly;
9673
9674     // speedy lookup for elements never to box adjust
9675     var noBoxAdjust = Roo.isStrict ? {
9676         select:1
9677     } : {
9678         input:1, select:1, textarea:1
9679     };
9680     if(Roo.isIE || Roo.isGecko){
9681         noBoxAdjust['button'] = 1;
9682     }
9683
9684
9685     Roo.EventManager.on(window, 'unload', function(){
9686         delete El.cache;
9687         delete El._flyweights;
9688     });
9689 })();
9690
9691
9692
9693
9694 if(Roo.DomQuery){
9695     Roo.Element.selectorFunction = Roo.DomQuery.select;
9696 }
9697
9698 Roo.Element.select = function(selector, unique, root){
9699     var els;
9700     if(typeof selector == "string"){
9701         els = Roo.Element.selectorFunction(selector, root);
9702     }else if(selector.length !== undefined){
9703         els = selector;
9704     }else{
9705         throw "Invalid selector";
9706     }
9707     if(unique === true){
9708         return new Roo.CompositeElement(els);
9709     }else{
9710         return new Roo.CompositeElementLite(els);
9711     }
9712 };
9713 /**
9714  * Selects elements based on the passed CSS selector to enable working on them as 1.
9715  * @param {String/Array} selector The CSS selector or an array of elements
9716  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9717  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9718  * @return {CompositeElementLite/CompositeElement}
9719  * @member Roo
9720  * @method select
9721  */
9722 Roo.select = Roo.Element.select;
9723
9724
9725
9726
9727
9728
9729
9730
9731
9732
9733
9734
9735
9736
9737 /*
9738  * Based on:
9739  * Ext JS Library 1.1.1
9740  * Copyright(c) 2006-2007, Ext JS, LLC.
9741  *
9742  * Originally Released Under LGPL - original licence link has changed is not relivant.
9743  *
9744  * Fork - LGPL
9745  * <script type="text/javascript">
9746  */
9747
9748
9749
9750 //Notifies Element that fx methods are available
9751 Roo.enableFx = true;
9752
9753 /**
9754  * @class Roo.Fx
9755  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9756  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9757  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9758  * Element effects to work.</p><br/>
9759  *
9760  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9761  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9762  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9763  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9764  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9765  * expected results and should be done with care.</p><br/>
9766  *
9767  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9768  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9769 <pre>
9770 Value  Description
9771 -----  -----------------------------
9772 tl     The top left corner
9773 t      The center of the top edge
9774 tr     The top right corner
9775 l      The center of the left edge
9776 r      The center of the right edge
9777 bl     The bottom left corner
9778 b      The center of the bottom edge
9779 br     The bottom right corner
9780 </pre>
9781  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9782  * below are common options that can be passed to any Fx method.</b>
9783  * @cfg {Function} callback A function called when the effect is finished
9784  * @cfg {Object} scope The scope of the effect function
9785  * @cfg {String} easing A valid Easing value for the effect
9786  * @cfg {String} afterCls A css class to apply after the effect
9787  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9788  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9789  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9790  * effects that end with the element being visually hidden, ignored otherwise)
9791  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9792  * a function which returns such a specification that will be applied to the Element after the effect finishes
9793  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9794  * @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
9795  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9796  */
9797 Roo.Fx = {
9798         /**
9799          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9800          * origin for the slide effect.  This function automatically handles wrapping the element with
9801          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9802          * Usage:
9803          *<pre><code>
9804 // default: slide the element in from the top
9805 el.slideIn();
9806
9807 // custom: slide the element in from the right with a 2-second duration
9808 el.slideIn('r', { duration: 2 });
9809
9810 // common config options shown with default values
9811 el.slideIn('t', {
9812     easing: 'easeOut',
9813     duration: .5
9814 });
9815 </code></pre>
9816          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9817          * @param {Object} options (optional) Object literal with any of the Fx config options
9818          * @return {Roo.Element} The Element
9819          */
9820     slideIn : function(anchor, o){
9821         var el = this.getFxEl();
9822         o = o || {};
9823
9824         el.queueFx(o, function(){
9825
9826             anchor = anchor || "t";
9827
9828             // fix display to visibility
9829             this.fixDisplay();
9830
9831             // restore values after effect
9832             var r = this.getFxRestore();
9833             var b = this.getBox();
9834             // fixed size for slide
9835             this.setSize(b);
9836
9837             // wrap if needed
9838             var wrap = this.fxWrap(r.pos, o, "hidden");
9839
9840             var st = this.dom.style;
9841             st.visibility = "visible";
9842             st.position = "absolute";
9843
9844             // clear out temp styles after slide and unwrap
9845             var after = function(){
9846                 el.fxUnwrap(wrap, r.pos, o);
9847                 st.width = r.width;
9848                 st.height = r.height;
9849                 el.afterFx(o);
9850             };
9851             // time to calc the positions
9852             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9853
9854             switch(anchor.toLowerCase()){
9855                 case "t":
9856                     wrap.setSize(b.width, 0);
9857                     st.left = st.bottom = "0";
9858                     a = {height: bh};
9859                 break;
9860                 case "l":
9861                     wrap.setSize(0, b.height);
9862                     st.right = st.top = "0";
9863                     a = {width: bw};
9864                 break;
9865                 case "r":
9866                     wrap.setSize(0, b.height);
9867                     wrap.setX(b.right);
9868                     st.left = st.top = "0";
9869                     a = {width: bw, points: pt};
9870                 break;
9871                 case "b":
9872                     wrap.setSize(b.width, 0);
9873                     wrap.setY(b.bottom);
9874                     st.left = st.top = "0";
9875                     a = {height: bh, points: pt};
9876                 break;
9877                 case "tl":
9878                     wrap.setSize(0, 0);
9879                     st.right = st.bottom = "0";
9880                     a = {width: bw, height: bh};
9881                 break;
9882                 case "bl":
9883                     wrap.setSize(0, 0);
9884                     wrap.setY(b.y+b.height);
9885                     st.right = st.top = "0";
9886                     a = {width: bw, height: bh, points: pt};
9887                 break;
9888                 case "br":
9889                     wrap.setSize(0, 0);
9890                     wrap.setXY([b.right, b.bottom]);
9891                     st.left = st.top = "0";
9892                     a = {width: bw, height: bh, points: pt};
9893                 break;
9894                 case "tr":
9895                     wrap.setSize(0, 0);
9896                     wrap.setX(b.x+b.width);
9897                     st.left = st.bottom = "0";
9898                     a = {width: bw, height: bh, points: pt};
9899                 break;
9900             }
9901             this.dom.style.visibility = "visible";
9902             wrap.show();
9903
9904             arguments.callee.anim = wrap.fxanim(a,
9905                 o,
9906                 'motion',
9907                 .5,
9908                 'easeOut', after);
9909         });
9910         return this;
9911     },
9912     
9913         /**
9914          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9915          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9916          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9917          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9918          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9919          * Usage:
9920          *<pre><code>
9921 // default: slide the element out to the top
9922 el.slideOut();
9923
9924 // custom: slide the element out to the right with a 2-second duration
9925 el.slideOut('r', { duration: 2 });
9926
9927 // common config options shown with default values
9928 el.slideOut('t', {
9929     easing: 'easeOut',
9930     duration: .5,
9931     remove: false,
9932     useDisplay: false
9933 });
9934 </code></pre>
9935          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9936          * @param {Object} options (optional) Object literal with any of the Fx config options
9937          * @return {Roo.Element} The Element
9938          */
9939     slideOut : function(anchor, o){
9940         var el = this.getFxEl();
9941         o = o || {};
9942
9943         el.queueFx(o, function(){
9944
9945             anchor = anchor || "t";
9946
9947             // restore values after effect
9948             var r = this.getFxRestore();
9949             
9950             var b = this.getBox();
9951             // fixed size for slide
9952             this.setSize(b);
9953
9954             // wrap if needed
9955             var wrap = this.fxWrap(r.pos, o, "visible");
9956
9957             var st = this.dom.style;
9958             st.visibility = "visible";
9959             st.position = "absolute";
9960
9961             wrap.setSize(b);
9962
9963             var after = function(){
9964                 if(o.useDisplay){
9965                     el.setDisplayed(false);
9966                 }else{
9967                     el.hide();
9968                 }
9969
9970                 el.fxUnwrap(wrap, r.pos, o);
9971
9972                 st.width = r.width;
9973                 st.height = r.height;
9974
9975                 el.afterFx(o);
9976             };
9977
9978             var a, zero = {to: 0};
9979             switch(anchor.toLowerCase()){
9980                 case "t":
9981                     st.left = st.bottom = "0";
9982                     a = {height: zero};
9983                 break;
9984                 case "l":
9985                     st.right = st.top = "0";
9986                     a = {width: zero};
9987                 break;
9988                 case "r":
9989                     st.left = st.top = "0";
9990                     a = {width: zero, points: {to:[b.right, b.y]}};
9991                 break;
9992                 case "b":
9993                     st.left = st.top = "0";
9994                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9995                 break;
9996                 case "tl":
9997                     st.right = st.bottom = "0";
9998                     a = {width: zero, height: zero};
9999                 break;
10000                 case "bl":
10001                     st.right = st.top = "0";
10002                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10003                 break;
10004                 case "br":
10005                     st.left = st.top = "0";
10006                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10007                 break;
10008                 case "tr":
10009                     st.left = st.bottom = "0";
10010                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10011                 break;
10012             }
10013
10014             arguments.callee.anim = wrap.fxanim(a,
10015                 o,
10016                 'motion',
10017                 .5,
10018                 "easeOut", after);
10019         });
10020         return this;
10021     },
10022
10023         /**
10024          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10025          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10026          * The element must be removed from the DOM using the 'remove' config option if desired.
10027          * Usage:
10028          *<pre><code>
10029 // default
10030 el.puff();
10031
10032 // common config options shown with default values
10033 el.puff({
10034     easing: 'easeOut',
10035     duration: .5,
10036     remove: false,
10037     useDisplay: false
10038 });
10039 </code></pre>
10040          * @param {Object} options (optional) Object literal with any of the Fx config options
10041          * @return {Roo.Element} The Element
10042          */
10043     puff : function(o){
10044         var el = this.getFxEl();
10045         o = o || {};
10046
10047         el.queueFx(o, function(){
10048             this.clearOpacity();
10049             this.show();
10050
10051             // restore values after effect
10052             var r = this.getFxRestore();
10053             var st = this.dom.style;
10054
10055             var after = function(){
10056                 if(o.useDisplay){
10057                     el.setDisplayed(false);
10058                 }else{
10059                     el.hide();
10060                 }
10061
10062                 el.clearOpacity();
10063
10064                 el.setPositioning(r.pos);
10065                 st.width = r.width;
10066                 st.height = r.height;
10067                 st.fontSize = '';
10068                 el.afterFx(o);
10069             };
10070
10071             var width = this.getWidth();
10072             var height = this.getHeight();
10073
10074             arguments.callee.anim = this.fxanim({
10075                     width : {to: this.adjustWidth(width * 2)},
10076                     height : {to: this.adjustHeight(height * 2)},
10077                     points : {by: [-(width * .5), -(height * .5)]},
10078                     opacity : {to: 0},
10079                     fontSize: {to:200, unit: "%"}
10080                 },
10081                 o,
10082                 'motion',
10083                 .5,
10084                 "easeOut", after);
10085         });
10086         return this;
10087     },
10088
10089         /**
10090          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10091          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10092          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10093          * Usage:
10094          *<pre><code>
10095 // default
10096 el.switchOff();
10097
10098 // all config options shown with default values
10099 el.switchOff({
10100     easing: 'easeIn',
10101     duration: .3,
10102     remove: false,
10103     useDisplay: false
10104 });
10105 </code></pre>
10106          * @param {Object} options (optional) Object literal with any of the Fx config options
10107          * @return {Roo.Element} The Element
10108          */
10109     switchOff : function(o){
10110         var el = this.getFxEl();
10111         o = o || {};
10112
10113         el.queueFx(o, function(){
10114             this.clearOpacity();
10115             this.clip();
10116
10117             // restore values after effect
10118             var r = this.getFxRestore();
10119             var st = this.dom.style;
10120
10121             var after = function(){
10122                 if(o.useDisplay){
10123                     el.setDisplayed(false);
10124                 }else{
10125                     el.hide();
10126                 }
10127
10128                 el.clearOpacity();
10129                 el.setPositioning(r.pos);
10130                 st.width = r.width;
10131                 st.height = r.height;
10132
10133                 el.afterFx(o);
10134             };
10135
10136             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10137                 this.clearOpacity();
10138                 (function(){
10139                     this.fxanim({
10140                         height:{to:1},
10141                         points:{by:[0, this.getHeight() * .5]}
10142                     }, o, 'motion', 0.3, 'easeIn', after);
10143                 }).defer(100, this);
10144             });
10145         });
10146         return this;
10147     },
10148
10149     /**
10150      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10151      * changed using the "attr" config option) and then fading back to the original color. If no original
10152      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10153      * Usage:
10154 <pre><code>
10155 // default: highlight background to yellow
10156 el.highlight();
10157
10158 // custom: highlight foreground text to blue for 2 seconds
10159 el.highlight("0000ff", { attr: 'color', duration: 2 });
10160
10161 // common config options shown with default values
10162 el.highlight("ffff9c", {
10163     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10164     endColor: (current color) or "ffffff",
10165     easing: 'easeIn',
10166     duration: 1
10167 });
10168 </code></pre>
10169      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10170      * @param {Object} options (optional) Object literal with any of the Fx config options
10171      * @return {Roo.Element} The Element
10172      */ 
10173     highlight : function(color, o){
10174         var el = this.getFxEl();
10175         o = o || {};
10176
10177         el.queueFx(o, function(){
10178             color = color || "ffff9c";
10179             attr = o.attr || "backgroundColor";
10180
10181             this.clearOpacity();
10182             this.show();
10183
10184             var origColor = this.getColor(attr);
10185             var restoreColor = this.dom.style[attr];
10186             endColor = (o.endColor || origColor) || "ffffff";
10187
10188             var after = function(){
10189                 el.dom.style[attr] = restoreColor;
10190                 el.afterFx(o);
10191             };
10192
10193             var a = {};
10194             a[attr] = {from: color, to: endColor};
10195             arguments.callee.anim = this.fxanim(a,
10196                 o,
10197                 'color',
10198                 1,
10199                 'easeIn', after);
10200         });
10201         return this;
10202     },
10203
10204    /**
10205     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10206     * Usage:
10207 <pre><code>
10208 // default: a single light blue ripple
10209 el.frame();
10210
10211 // custom: 3 red ripples lasting 3 seconds total
10212 el.frame("ff0000", 3, { duration: 3 });
10213
10214 // common config options shown with default values
10215 el.frame("C3DAF9", 1, {
10216     duration: 1 //duration of entire animation (not each individual ripple)
10217     // Note: Easing is not configurable and will be ignored if included
10218 });
10219 </code></pre>
10220     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10221     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10222     * @param {Object} options (optional) Object literal with any of the Fx config options
10223     * @return {Roo.Element} The Element
10224     */
10225     frame : function(color, count, o){
10226         var el = this.getFxEl();
10227         o = o || {};
10228
10229         el.queueFx(o, function(){
10230             color = color || "#C3DAF9";
10231             if(color.length == 6){
10232                 color = "#" + color;
10233             }
10234             count = count || 1;
10235             duration = o.duration || 1;
10236             this.show();
10237
10238             var b = this.getBox();
10239             var animFn = function(){
10240                 var proxy = this.createProxy({
10241
10242                      style:{
10243                         visbility:"hidden",
10244                         position:"absolute",
10245                         "z-index":"35000", // yee haw
10246                         border:"0px solid " + color
10247                      }
10248                   });
10249                 var scale = Roo.isBorderBox ? 2 : 1;
10250                 proxy.animate({
10251                     top:{from:b.y, to:b.y - 20},
10252                     left:{from:b.x, to:b.x - 20},
10253                     borderWidth:{from:0, to:10},
10254                     opacity:{from:1, to:0},
10255                     height:{from:b.height, to:(b.height + (20*scale))},
10256                     width:{from:b.width, to:(b.width + (20*scale))}
10257                 }, duration, function(){
10258                     proxy.remove();
10259                 });
10260                 if(--count > 0){
10261                      animFn.defer((duration/2)*1000, this);
10262                 }else{
10263                     el.afterFx(o);
10264                 }
10265             };
10266             animFn.call(this);
10267         });
10268         return this;
10269     },
10270
10271    /**
10272     * Creates a pause before any subsequent queued effects begin.  If there are
10273     * no effects queued after the pause it will have no effect.
10274     * Usage:
10275 <pre><code>
10276 el.pause(1);
10277 </code></pre>
10278     * @param {Number} seconds The length of time to pause (in seconds)
10279     * @return {Roo.Element} The Element
10280     */
10281     pause : function(seconds){
10282         var el = this.getFxEl();
10283         var o = {};
10284
10285         el.queueFx(o, function(){
10286             setTimeout(function(){
10287                 el.afterFx(o);
10288             }, seconds * 1000);
10289         });
10290         return this;
10291     },
10292
10293    /**
10294     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10295     * using the "endOpacity" config option.
10296     * Usage:
10297 <pre><code>
10298 // default: fade in from opacity 0 to 100%
10299 el.fadeIn();
10300
10301 // custom: fade in from opacity 0 to 75% over 2 seconds
10302 el.fadeIn({ endOpacity: .75, duration: 2});
10303
10304 // common config options shown with default values
10305 el.fadeIn({
10306     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10307     easing: 'easeOut',
10308     duration: .5
10309 });
10310 </code></pre>
10311     * @param {Object} options (optional) Object literal with any of the Fx config options
10312     * @return {Roo.Element} The Element
10313     */
10314     fadeIn : function(o){
10315         var el = this.getFxEl();
10316         o = o || {};
10317         el.queueFx(o, function(){
10318             this.setOpacity(0);
10319             this.fixDisplay();
10320             this.dom.style.visibility = 'visible';
10321             var to = o.endOpacity || 1;
10322             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10323                 o, null, .5, "easeOut", function(){
10324                 if(to == 1){
10325                     this.clearOpacity();
10326                 }
10327                 el.afterFx(o);
10328             });
10329         });
10330         return this;
10331     },
10332
10333    /**
10334     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10335     * using the "endOpacity" config option.
10336     * Usage:
10337 <pre><code>
10338 // default: fade out from the element's current opacity to 0
10339 el.fadeOut();
10340
10341 // custom: fade out from the element's current opacity to 25% over 2 seconds
10342 el.fadeOut({ endOpacity: .25, duration: 2});
10343
10344 // common config options shown with default values
10345 el.fadeOut({
10346     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10347     easing: 'easeOut',
10348     duration: .5
10349     remove: false,
10350     useDisplay: false
10351 });
10352 </code></pre>
10353     * @param {Object} options (optional) Object literal with any of the Fx config options
10354     * @return {Roo.Element} The Element
10355     */
10356     fadeOut : function(o){
10357         var el = this.getFxEl();
10358         o = o || {};
10359         el.queueFx(o, function(){
10360             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10361                 o, null, .5, "easeOut", function(){
10362                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10363                      this.dom.style.display = "none";
10364                 }else{
10365                      this.dom.style.visibility = "hidden";
10366                 }
10367                 this.clearOpacity();
10368                 el.afterFx(o);
10369             });
10370         });
10371         return this;
10372     },
10373
10374    /**
10375     * Animates the transition of an element's dimensions from a starting height/width
10376     * to an ending height/width.
10377     * Usage:
10378 <pre><code>
10379 // change height and width to 100x100 pixels
10380 el.scale(100, 100);
10381
10382 // common config options shown with default values.  The height and width will default to
10383 // the element's existing values if passed as null.
10384 el.scale(
10385     [element's width],
10386     [element's height], {
10387     easing: 'easeOut',
10388     duration: .35
10389 });
10390 </code></pre>
10391     * @param {Number} width  The new width (pass undefined to keep the original width)
10392     * @param {Number} height  The new height (pass undefined to keep the original height)
10393     * @param {Object} options (optional) Object literal with any of the Fx config options
10394     * @return {Roo.Element} The Element
10395     */
10396     scale : function(w, h, o){
10397         this.shift(Roo.apply({}, o, {
10398             width: w,
10399             height: h
10400         }));
10401         return this;
10402     },
10403
10404    /**
10405     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10406     * Any of these properties not specified in the config object will not be changed.  This effect 
10407     * requires that at least one new dimension, position or opacity setting must be passed in on
10408     * the config object in order for the function to have any effect.
10409     * Usage:
10410 <pre><code>
10411 // slide the element horizontally to x position 200 while changing the height and opacity
10412 el.shift({ x: 200, height: 50, opacity: .8 });
10413
10414 // common config options shown with default values.
10415 el.shift({
10416     width: [element's width],
10417     height: [element's height],
10418     x: [element's x position],
10419     y: [element's y position],
10420     opacity: [element's opacity],
10421     easing: 'easeOut',
10422     duration: .35
10423 });
10424 </code></pre>
10425     * @param {Object} options  Object literal with any of the Fx config options
10426     * @return {Roo.Element} The Element
10427     */
10428     shift : function(o){
10429         var el = this.getFxEl();
10430         o = o || {};
10431         el.queueFx(o, function(){
10432             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10433             if(w !== undefined){
10434                 a.width = {to: this.adjustWidth(w)};
10435             }
10436             if(h !== undefined){
10437                 a.height = {to: this.adjustHeight(h)};
10438             }
10439             if(x !== undefined || y !== undefined){
10440                 a.points = {to: [
10441                     x !== undefined ? x : this.getX(),
10442                     y !== undefined ? y : this.getY()
10443                 ]};
10444             }
10445             if(op !== undefined){
10446                 a.opacity = {to: op};
10447             }
10448             if(o.xy !== undefined){
10449                 a.points = {to: o.xy};
10450             }
10451             arguments.callee.anim = this.fxanim(a,
10452                 o, 'motion', .35, "easeOut", function(){
10453                 el.afterFx(o);
10454             });
10455         });
10456         return this;
10457     },
10458
10459         /**
10460          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10461          * ending point of the effect.
10462          * Usage:
10463          *<pre><code>
10464 // default: slide the element downward while fading out
10465 el.ghost();
10466
10467 // custom: slide the element out to the right with a 2-second duration
10468 el.ghost('r', { duration: 2 });
10469
10470 // common config options shown with default values
10471 el.ghost('b', {
10472     easing: 'easeOut',
10473     duration: .5
10474     remove: false,
10475     useDisplay: false
10476 });
10477 </code></pre>
10478          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10479          * @param {Object} options (optional) Object literal with any of the Fx config options
10480          * @return {Roo.Element} The Element
10481          */
10482     ghost : function(anchor, o){
10483         var el = this.getFxEl();
10484         o = o || {};
10485
10486         el.queueFx(o, function(){
10487             anchor = anchor || "b";
10488
10489             // restore values after effect
10490             var r = this.getFxRestore();
10491             var w = this.getWidth(),
10492                 h = this.getHeight();
10493
10494             var st = this.dom.style;
10495
10496             var after = function(){
10497                 if(o.useDisplay){
10498                     el.setDisplayed(false);
10499                 }else{
10500                     el.hide();
10501                 }
10502
10503                 el.clearOpacity();
10504                 el.setPositioning(r.pos);
10505                 st.width = r.width;
10506                 st.height = r.height;
10507
10508                 el.afterFx(o);
10509             };
10510
10511             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10512             switch(anchor.toLowerCase()){
10513                 case "t":
10514                     pt.by = [0, -h];
10515                 break;
10516                 case "l":
10517                     pt.by = [-w, 0];
10518                 break;
10519                 case "r":
10520                     pt.by = [w, 0];
10521                 break;
10522                 case "b":
10523                     pt.by = [0, h];
10524                 break;
10525                 case "tl":
10526                     pt.by = [-w, -h];
10527                 break;
10528                 case "bl":
10529                     pt.by = [-w, h];
10530                 break;
10531                 case "br":
10532                     pt.by = [w, h];
10533                 break;
10534                 case "tr":
10535                     pt.by = [w, -h];
10536                 break;
10537             }
10538
10539             arguments.callee.anim = this.fxanim(a,
10540                 o,
10541                 'motion',
10542                 .5,
10543                 "easeOut", after);
10544         });
10545         return this;
10546     },
10547
10548         /**
10549          * Ensures that all effects queued after syncFx is called on the element are
10550          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10551          * @return {Roo.Element} The Element
10552          */
10553     syncFx : function(){
10554         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10555             block : false,
10556             concurrent : true,
10557             stopFx : false
10558         });
10559         return this;
10560     },
10561
10562         /**
10563          * Ensures that all effects queued after sequenceFx is called on the element are
10564          * run in sequence.  This is the opposite of {@link #syncFx}.
10565          * @return {Roo.Element} The Element
10566          */
10567     sequenceFx : function(){
10568         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10569             block : false,
10570             concurrent : false,
10571             stopFx : false
10572         });
10573         return this;
10574     },
10575
10576         /* @private */
10577     nextFx : function(){
10578         var ef = this.fxQueue[0];
10579         if(ef){
10580             ef.call(this);
10581         }
10582     },
10583
10584         /**
10585          * Returns true if the element has any effects actively running or queued, else returns false.
10586          * @return {Boolean} True if element has active effects, else false
10587          */
10588     hasActiveFx : function(){
10589         return this.fxQueue && this.fxQueue[0];
10590     },
10591
10592         /**
10593          * Stops any running effects and clears the element's internal effects queue if it contains
10594          * any additional effects that haven't started yet.
10595          * @return {Roo.Element} The Element
10596          */
10597     stopFx : function(){
10598         if(this.hasActiveFx()){
10599             var cur = this.fxQueue[0];
10600             if(cur && cur.anim && cur.anim.isAnimated()){
10601                 this.fxQueue = [cur]; // clear out others
10602                 cur.anim.stop(true);
10603             }
10604         }
10605         return this;
10606     },
10607
10608         /* @private */
10609     beforeFx : function(o){
10610         if(this.hasActiveFx() && !o.concurrent){
10611            if(o.stopFx){
10612                this.stopFx();
10613                return true;
10614            }
10615            return false;
10616         }
10617         return true;
10618     },
10619
10620         /**
10621          * Returns true if the element is currently blocking so that no other effect can be queued
10622          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10623          * used to ensure that an effect initiated by a user action runs to completion prior to the
10624          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10625          * @return {Boolean} True if blocking, else false
10626          */
10627     hasFxBlock : function(){
10628         var q = this.fxQueue;
10629         return q && q[0] && q[0].block;
10630     },
10631
10632         /* @private */
10633     queueFx : function(o, fn){
10634         if(!this.fxQueue){
10635             this.fxQueue = [];
10636         }
10637         if(!this.hasFxBlock()){
10638             Roo.applyIf(o, this.fxDefaults);
10639             if(!o.concurrent){
10640                 var run = this.beforeFx(o);
10641                 fn.block = o.block;
10642                 this.fxQueue.push(fn);
10643                 if(run){
10644                     this.nextFx();
10645                 }
10646             }else{
10647                 fn.call(this);
10648             }
10649         }
10650         return this;
10651     },
10652
10653         /* @private */
10654     fxWrap : function(pos, o, vis){
10655         var wrap;
10656         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10657             var wrapXY;
10658             if(o.fixPosition){
10659                 wrapXY = this.getXY();
10660             }
10661             var div = document.createElement("div");
10662             div.style.visibility = vis;
10663             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10664             wrap.setPositioning(pos);
10665             if(wrap.getStyle("position") == "static"){
10666                 wrap.position("relative");
10667             }
10668             this.clearPositioning('auto');
10669             wrap.clip();
10670             wrap.dom.appendChild(this.dom);
10671             if(wrapXY){
10672                 wrap.setXY(wrapXY);
10673             }
10674         }
10675         return wrap;
10676     },
10677
10678         /* @private */
10679     fxUnwrap : function(wrap, pos, o){
10680         this.clearPositioning();
10681         this.setPositioning(pos);
10682         if(!o.wrap){
10683             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10684             wrap.remove();
10685         }
10686     },
10687
10688         /* @private */
10689     getFxRestore : function(){
10690         var st = this.dom.style;
10691         return {pos: this.getPositioning(), width: st.width, height : st.height};
10692     },
10693
10694         /* @private */
10695     afterFx : function(o){
10696         if(o.afterStyle){
10697             this.applyStyles(o.afterStyle);
10698         }
10699         if(o.afterCls){
10700             this.addClass(o.afterCls);
10701         }
10702         if(o.remove === true){
10703             this.remove();
10704         }
10705         Roo.callback(o.callback, o.scope, [this]);
10706         if(!o.concurrent){
10707             this.fxQueue.shift();
10708             this.nextFx();
10709         }
10710     },
10711
10712         /* @private */
10713     getFxEl : function(){ // support for composite element fx
10714         return Roo.get(this.dom);
10715     },
10716
10717         /* @private */
10718     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10719         animType = animType || 'run';
10720         opt = opt || {};
10721         var anim = Roo.lib.Anim[animType](
10722             this.dom, args,
10723             (opt.duration || defaultDur) || .35,
10724             (opt.easing || defaultEase) || 'easeOut',
10725             function(){
10726                 Roo.callback(cb, this);
10727             },
10728             this
10729         );
10730         opt.anim = anim;
10731         return anim;
10732     }
10733 };
10734
10735 // backwords compat
10736 Roo.Fx.resize = Roo.Fx.scale;
10737
10738 //When included, Roo.Fx is automatically applied to Element so that all basic
10739 //effects are available directly via the Element API
10740 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10741  * Based on:
10742  * Ext JS Library 1.1.1
10743  * Copyright(c) 2006-2007, Ext JS, LLC.
10744  *
10745  * Originally Released Under LGPL - original licence link has changed is not relivant.
10746  *
10747  * Fork - LGPL
10748  * <script type="text/javascript">
10749  */
10750
10751
10752 /**
10753  * @class Roo.CompositeElement
10754  * Standard composite class. Creates a Roo.Element for every element in the collection.
10755  * <br><br>
10756  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10757  * actions will be performed on all the elements in this collection.</b>
10758  * <br><br>
10759  * All methods return <i>this</i> and can be chained.
10760  <pre><code>
10761  var els = Roo.select("#some-el div.some-class", true);
10762  // or select directly from an existing element
10763  var el = Roo.get('some-el');
10764  el.select('div.some-class', true);
10765
10766  els.setWidth(100); // all elements become 100 width
10767  els.hide(true); // all elements fade out and hide
10768  // or
10769  els.setWidth(100).hide(true);
10770  </code></pre>
10771  */
10772 Roo.CompositeElement = function(els){
10773     this.elements = [];
10774     this.addElements(els);
10775 };
10776 Roo.CompositeElement.prototype = {
10777     isComposite: true,
10778     addElements : function(els){
10779         if(!els) return this;
10780         if(typeof els == "string"){
10781             els = Roo.Element.selectorFunction(els);
10782         }
10783         var yels = this.elements;
10784         var index = yels.length-1;
10785         for(var i = 0, len = els.length; i < len; i++) {
10786                 yels[++index] = Roo.get(els[i]);
10787         }
10788         return this;
10789     },
10790
10791     /**
10792     * Clears this composite and adds the elements returned by the passed selector.
10793     * @param {String/Array} els A string CSS selector, an array of elements or an element
10794     * @return {CompositeElement} this
10795     */
10796     fill : function(els){
10797         this.elements = [];
10798         this.add(els);
10799         return this;
10800     },
10801
10802     /**
10803     * Filters this composite to only elements that match the passed selector.
10804     * @param {String} selector A string CSS selector
10805     * @return {CompositeElement} this
10806     */
10807     filter : function(selector){
10808         var els = [];
10809         this.each(function(el){
10810             if(el.is(selector)){
10811                 els[els.length] = el.dom;
10812             }
10813         });
10814         this.fill(els);
10815         return this;
10816     },
10817
10818     invoke : function(fn, args){
10819         var els = this.elements;
10820         for(var i = 0, len = els.length; i < len; i++) {
10821                 Roo.Element.prototype[fn].apply(els[i], args);
10822         }
10823         return this;
10824     },
10825     /**
10826     * Adds elements to this composite.
10827     * @param {String/Array} els A string CSS selector, an array of elements or an element
10828     * @return {CompositeElement} this
10829     */
10830     add : function(els){
10831         if(typeof els == "string"){
10832             this.addElements(Roo.Element.selectorFunction(els));
10833         }else if(els.length !== undefined){
10834             this.addElements(els);
10835         }else{
10836             this.addElements([els]);
10837         }
10838         return this;
10839     },
10840     /**
10841     * Calls the passed function passing (el, this, index) for each element in this composite.
10842     * @param {Function} fn The function to call
10843     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10844     * @return {CompositeElement} this
10845     */
10846     each : function(fn, scope){
10847         var els = this.elements;
10848         for(var i = 0, len = els.length; i < len; i++){
10849             if(fn.call(scope || els[i], els[i], this, i) === false) {
10850                 break;
10851             }
10852         }
10853         return this;
10854     },
10855
10856     /**
10857      * Returns the Element object at the specified index
10858      * @param {Number} index
10859      * @return {Roo.Element}
10860      */
10861     item : function(index){
10862         return this.elements[index] || null;
10863     },
10864
10865     /**
10866      * Returns the first Element
10867      * @return {Roo.Element}
10868      */
10869     first : function(){
10870         return this.item(0);
10871     },
10872
10873     /**
10874      * Returns the last Element
10875      * @return {Roo.Element}
10876      */
10877     last : function(){
10878         return this.item(this.elements.length-1);
10879     },
10880
10881     /**
10882      * Returns the number of elements in this composite
10883      * @return Number
10884      */
10885     getCount : function(){
10886         return this.elements.length;
10887     },
10888
10889     /**
10890      * Returns true if this composite contains the passed element
10891      * @return Boolean
10892      */
10893     contains : function(el){
10894         return this.indexOf(el) !== -1;
10895     },
10896
10897     /**
10898      * Returns true if this composite contains the passed element
10899      * @return Boolean
10900      */
10901     indexOf : function(el){
10902         return this.elements.indexOf(Roo.get(el));
10903     },
10904
10905
10906     /**
10907     * Removes the specified element(s).
10908     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10909     * or an array of any of those.
10910     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10911     * @return {CompositeElement} this
10912     */
10913     removeElement : function(el, removeDom){
10914         if(el instanceof Array){
10915             for(var i = 0, len = el.length; i < len; i++){
10916                 this.removeElement(el[i]);
10917             }
10918             return this;
10919         }
10920         var index = typeof el == 'number' ? el : this.indexOf(el);
10921         if(index !== -1){
10922             if(removeDom){
10923                 var d = this.elements[index];
10924                 if(d.dom){
10925                     d.remove();
10926                 }else{
10927                     d.parentNode.removeChild(d);
10928                 }
10929             }
10930             this.elements.splice(index, 1);
10931         }
10932         return this;
10933     },
10934
10935     /**
10936     * Replaces the specified element with the passed element.
10937     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10938     * to replace.
10939     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10940     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10941     * @return {CompositeElement} this
10942     */
10943     replaceElement : function(el, replacement, domReplace){
10944         var index = typeof el == 'number' ? el : this.indexOf(el);
10945         if(index !== -1){
10946             if(domReplace){
10947                 this.elements[index].replaceWith(replacement);
10948             }else{
10949                 this.elements.splice(index, 1, Roo.get(replacement))
10950             }
10951         }
10952         return this;
10953     },
10954
10955     /**
10956      * Removes all elements.
10957      */
10958     clear : function(){
10959         this.elements = [];
10960     }
10961 };
10962 (function(){
10963     Roo.CompositeElement.createCall = function(proto, fnName){
10964         if(!proto[fnName]){
10965             proto[fnName] = function(){
10966                 return this.invoke(fnName, arguments);
10967             };
10968         }
10969     };
10970     for(var fnName in Roo.Element.prototype){
10971         if(typeof Roo.Element.prototype[fnName] == "function"){
10972             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10973         }
10974     };
10975 })();
10976 /*
10977  * Based on:
10978  * Ext JS Library 1.1.1
10979  * Copyright(c) 2006-2007, Ext JS, LLC.
10980  *
10981  * Originally Released Under LGPL - original licence link has changed is not relivant.
10982  *
10983  * Fork - LGPL
10984  * <script type="text/javascript">
10985  */
10986
10987 /**
10988  * @class Roo.CompositeElementLite
10989  * @extends Roo.CompositeElement
10990  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10991  <pre><code>
10992  var els = Roo.select("#some-el div.some-class");
10993  // or select directly from an existing element
10994  var el = Roo.get('some-el');
10995  el.select('div.some-class');
10996
10997  els.setWidth(100); // all elements become 100 width
10998  els.hide(true); // all elements fade out and hide
10999  // or
11000  els.setWidth(100).hide(true);
11001  </code></pre><br><br>
11002  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11003  * actions will be performed on all the elements in this collection.</b>
11004  */
11005 Roo.CompositeElementLite = function(els){
11006     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11007     this.el = new Roo.Element.Flyweight();
11008 };
11009 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11010     addElements : function(els){
11011         if(els){
11012             if(els instanceof Array){
11013                 this.elements = this.elements.concat(els);
11014             }else{
11015                 var yels = this.elements;
11016                 var index = yels.length-1;
11017                 for(var i = 0, len = els.length; i < len; i++) {
11018                     yels[++index] = els[i];
11019                 }
11020             }
11021         }
11022         return this;
11023     },
11024     invoke : function(fn, args){
11025         var els = this.elements;
11026         var el = this.el;
11027         for(var i = 0, len = els.length; i < len; i++) {
11028             el.dom = els[i];
11029                 Roo.Element.prototype[fn].apply(el, args);
11030         }
11031         return this;
11032     },
11033     /**
11034      * Returns a flyweight Element of the dom element object at the specified index
11035      * @param {Number} index
11036      * @return {Roo.Element}
11037      */
11038     item : function(index){
11039         if(!this.elements[index]){
11040             return null;
11041         }
11042         this.el.dom = this.elements[index];
11043         return this.el;
11044     },
11045
11046     // fixes scope with flyweight
11047     addListener : function(eventName, handler, scope, opt){
11048         var els = this.elements;
11049         for(var i = 0, len = els.length; i < len; i++) {
11050             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11051         }
11052         return this;
11053     },
11054
11055     /**
11056     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11057     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11058     * a reference to the dom node, use el.dom.</b>
11059     * @param {Function} fn The function to call
11060     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11061     * @return {CompositeElement} this
11062     */
11063     each : function(fn, scope){
11064         var els = this.elements;
11065         var el = this.el;
11066         for(var i = 0, len = els.length; i < len; i++){
11067             el.dom = els[i];
11068                 if(fn.call(scope || el, el, this, i) === false){
11069                 break;
11070             }
11071         }
11072         return this;
11073     },
11074
11075     indexOf : function(el){
11076         return this.elements.indexOf(Roo.getDom(el));
11077     },
11078
11079     replaceElement : function(el, replacement, domReplace){
11080         var index = typeof el == 'number' ? el : this.indexOf(el);
11081         if(index !== -1){
11082             replacement = Roo.getDom(replacement);
11083             if(domReplace){
11084                 var d = this.elements[index];
11085                 d.parentNode.insertBefore(replacement, d);
11086                 d.parentNode.removeChild(d);
11087             }
11088             this.elements.splice(index, 1, replacement);
11089         }
11090         return this;
11091     }
11092 });
11093 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11094
11095 /*
11096  * Based on:
11097  * Ext JS Library 1.1.1
11098  * Copyright(c) 2006-2007, Ext JS, LLC.
11099  *
11100  * Originally Released Under LGPL - original licence link has changed is not relivant.
11101  *
11102  * Fork - LGPL
11103  * <script type="text/javascript">
11104  */
11105
11106  
11107
11108 /**
11109  * @class Roo.data.Connection
11110  * @extends Roo.util.Observable
11111  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11112  * either to a configured URL, or to a URL specified at request time.<br><br>
11113  * <p>
11114  * Requests made by this class are asynchronous, and will return immediately. No data from
11115  * the server will be available to the statement immediately following the {@link #request} call.
11116  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11117  * <p>
11118  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11119  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11120  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11121  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11122  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11123  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11124  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11125  * standard DOM methods.
11126  * @constructor
11127  * @param {Object} config a configuration object.
11128  */
11129 Roo.data.Connection = function(config){
11130     Roo.apply(this, config);
11131     this.addEvents({
11132         /**
11133          * @event beforerequest
11134          * Fires before a network request is made to retrieve a data object.
11135          * @param {Connection} conn This Connection object.
11136          * @param {Object} options The options config object passed to the {@link #request} method.
11137          */
11138         "beforerequest" : true,
11139         /**
11140          * @event requestcomplete
11141          * Fires if the request was successfully completed.
11142          * @param {Connection} conn This Connection object.
11143          * @param {Object} response The XHR object containing the response data.
11144          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11145          * @param {Object} options The options config object passed to the {@link #request} method.
11146          */
11147         "requestcomplete" : true,
11148         /**
11149          * @event requestexception
11150          * Fires if an error HTTP status was returned from the server.
11151          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11152          * @param {Connection} conn This Connection object.
11153          * @param {Object} response The XHR object containing the response data.
11154          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11155          * @param {Object} options The options config object passed to the {@link #request} method.
11156          */
11157         "requestexception" : true
11158     });
11159     Roo.data.Connection.superclass.constructor.call(this);
11160 };
11161
11162 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11163     /**
11164      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11165      */
11166     /**
11167      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11168      * extra parameters to each request made by this object. (defaults to undefined)
11169      */
11170     /**
11171      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11172      *  to each request made by this object. (defaults to undefined)
11173      */
11174     /**
11175      * @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)
11176      */
11177     /**
11178      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11179      */
11180     timeout : 30000,
11181     /**
11182      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11183      * @type Boolean
11184      */
11185     autoAbort:false,
11186
11187     /**
11188      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11189      * @type Boolean
11190      */
11191     disableCaching: true,
11192
11193     /**
11194      * Sends an HTTP request to a remote server.
11195      * @param {Object} options An object which may contain the following properties:<ul>
11196      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11197      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11198      * request, a url encoded string or a function to call to get either.</li>
11199      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11200      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11201      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11202      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11203      * <li>options {Object} The parameter to the request call.</li>
11204      * <li>success {Boolean} True if the request succeeded.</li>
11205      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11206      * </ul></li>
11207      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11208      * The callback is passed the following parameters:<ul>
11209      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11210      * <li>options {Object} The parameter to the request call.</li>
11211      * </ul></li>
11212      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11213      * The callback is passed the following parameters:<ul>
11214      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11215      * <li>options {Object} The parameter to the request call.</li>
11216      * </ul></li>
11217      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11218      * for the callback function. Defaults to the browser window.</li>
11219      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11220      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11221      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11222      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11223      * params for the post data. Any params will be appended to the URL.</li>
11224      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11225      * </ul>
11226      * @return {Number} transactionId
11227      */
11228     request : function(o){
11229         if(this.fireEvent("beforerequest", this, o) !== false){
11230             var p = o.params;
11231
11232             if(typeof p == "function"){
11233                 p = p.call(o.scope||window, o);
11234             }
11235             if(typeof p == "object"){
11236                 p = Roo.urlEncode(o.params);
11237             }
11238             if(this.extraParams){
11239                 var extras = Roo.urlEncode(this.extraParams);
11240                 p = p ? (p + '&' + extras) : extras;
11241             }
11242
11243             var url = o.url || this.url;
11244             if(typeof url == 'function'){
11245                 url = url.call(o.scope||window, o);
11246             }
11247
11248             if(o.form){
11249                 var form = Roo.getDom(o.form);
11250                 url = url || form.action;
11251
11252                 var enctype = form.getAttribute("enctype");
11253                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11254                     return this.doFormUpload(o, p, url);
11255                 }
11256                 var f = Roo.lib.Ajax.serializeForm(form);
11257                 p = p ? (p + '&' + f) : f;
11258             }
11259
11260             var hs = o.headers;
11261             if(this.defaultHeaders){
11262                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11263                 if(!o.headers){
11264                     o.headers = hs;
11265                 }
11266             }
11267
11268             var cb = {
11269                 success: this.handleResponse,
11270                 failure: this.handleFailure,
11271                 scope: this,
11272                 argument: {options: o},
11273                 timeout : this.timeout
11274             };
11275
11276             var method = o.method||this.method||(p ? "POST" : "GET");
11277
11278             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11279                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11280             }
11281
11282             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11283                 if(o.autoAbort){
11284                     this.abort();
11285                 }
11286             }else if(this.autoAbort !== false){
11287                 this.abort();
11288             }
11289
11290             if((method == 'GET' && p) || o.xmlData){
11291                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11292                 p = '';
11293             }
11294             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11295             return this.transId;
11296         }else{
11297             Roo.callback(o.callback, o.scope, [o, null, null]);
11298             return null;
11299         }
11300     },
11301
11302     /**
11303      * Determine whether this object has a request outstanding.
11304      * @param {Number} transactionId (Optional) defaults to the last transaction
11305      * @return {Boolean} True if there is an outstanding request.
11306      */
11307     isLoading : function(transId){
11308         if(transId){
11309             return Roo.lib.Ajax.isCallInProgress(transId);
11310         }else{
11311             return this.transId ? true : false;
11312         }
11313     },
11314
11315     /**
11316      * Aborts any outstanding request.
11317      * @param {Number} transactionId (Optional) defaults to the last transaction
11318      */
11319     abort : function(transId){
11320         if(transId || this.isLoading()){
11321             Roo.lib.Ajax.abort(transId || this.transId);
11322         }
11323     },
11324
11325     // private
11326     handleResponse : function(response){
11327         this.transId = false;
11328         var options = response.argument.options;
11329         response.argument = options ? options.argument : null;
11330         this.fireEvent("requestcomplete", this, response, options);
11331         Roo.callback(options.success, options.scope, [response, options]);
11332         Roo.callback(options.callback, options.scope, [options, true, response]);
11333     },
11334
11335     // private
11336     handleFailure : function(response, e){
11337         this.transId = false;
11338         var options = response.argument.options;
11339         response.argument = options ? options.argument : null;
11340         this.fireEvent("requestexception", this, response, options, e);
11341         Roo.callback(options.failure, options.scope, [response, options]);
11342         Roo.callback(options.callback, options.scope, [options, false, response]);
11343     },
11344
11345     // private
11346     doFormUpload : function(o, ps, url){
11347         var id = Roo.id();
11348         var frame = document.createElement('iframe');
11349         frame.id = id;
11350         frame.name = id;
11351         frame.className = 'x-hidden';
11352         if(Roo.isIE){
11353             frame.src = Roo.SSL_SECURE_URL;
11354         }
11355         document.body.appendChild(frame);
11356
11357         if(Roo.isIE){
11358            document.frames[id].name = id;
11359         }
11360
11361         var form = Roo.getDom(o.form);
11362         form.target = id;
11363         form.method = 'POST';
11364         form.enctype = form.encoding = 'multipart/form-data';
11365         if(url){
11366             form.action = url;
11367         }
11368
11369         var hiddens, hd;
11370         if(ps){ // add dynamic params
11371             hiddens = [];
11372             ps = Roo.urlDecode(ps, false);
11373             for(var k in ps){
11374                 if(ps.hasOwnProperty(k)){
11375                     hd = document.createElement('input');
11376                     hd.type = 'hidden';
11377                     hd.name = k;
11378                     hd.value = ps[k];
11379                     form.appendChild(hd);
11380                     hiddens.push(hd);
11381                 }
11382             }
11383         }
11384
11385         function cb(){
11386             var r = {  // bogus response object
11387                 responseText : '',
11388                 responseXML : null
11389             };
11390
11391             r.argument = o ? o.argument : null;
11392
11393             try { //
11394                 var doc;
11395                 if(Roo.isIE){
11396                     doc = frame.contentWindow.document;
11397                 }else {
11398                     doc = (frame.contentDocument || window.frames[id].document);
11399                 }
11400                 if(doc && doc.body){
11401                     r.responseText = doc.body.innerHTML;
11402                 }
11403                 if(doc && doc.XMLDocument){
11404                     r.responseXML = doc.XMLDocument;
11405                 }else {
11406                     r.responseXML = doc;
11407                 }
11408             }
11409             catch(e) {
11410                 // ignore
11411             }
11412
11413             Roo.EventManager.removeListener(frame, 'load', cb, this);
11414
11415             this.fireEvent("requestcomplete", this, r, o);
11416             Roo.callback(o.success, o.scope, [r, o]);
11417             Roo.callback(o.callback, o.scope, [o, true, r]);
11418
11419             setTimeout(function(){document.body.removeChild(frame);}, 100);
11420         }
11421
11422         Roo.EventManager.on(frame, 'load', cb, this);
11423         form.submit();
11424
11425         if(hiddens){ // remove dynamic params
11426             for(var i = 0, len = hiddens.length; i < len; i++){
11427                 form.removeChild(hiddens[i]);
11428             }
11429         }
11430     }
11431 });
11432
11433 /**
11434  * @class Roo.Ajax
11435  * @extends Roo.data.Connection
11436  * Global Ajax request class.
11437  *
11438  * @singleton
11439  */
11440 Roo.Ajax = new Roo.data.Connection({
11441     // fix up the docs
11442    /**
11443      * @cfg {String} url @hide
11444      */
11445     /**
11446      * @cfg {Object} extraParams @hide
11447      */
11448     /**
11449      * @cfg {Object} defaultHeaders @hide
11450      */
11451     /**
11452      * @cfg {String} method (Optional) @hide
11453      */
11454     /**
11455      * @cfg {Number} timeout (Optional) @hide
11456      */
11457     /**
11458      * @cfg {Boolean} autoAbort (Optional) @hide
11459      */
11460
11461     /**
11462      * @cfg {Boolean} disableCaching (Optional) @hide
11463      */
11464
11465     /**
11466      * @property  disableCaching
11467      * True to add a unique cache-buster param to GET requests. (defaults to true)
11468      * @type Boolean
11469      */
11470     /**
11471      * @property  url
11472      * The default URL to be used for requests to the server. (defaults to undefined)
11473      * @type String
11474      */
11475     /**
11476      * @property  extraParams
11477      * An object containing properties which are used as
11478      * extra parameters to each request made by this object. (defaults to undefined)
11479      * @type Object
11480      */
11481     /**
11482      * @property  defaultHeaders
11483      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11484      * @type Object
11485      */
11486     /**
11487      * @property  method
11488      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11489      * @type String
11490      */
11491     /**
11492      * @property  timeout
11493      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11494      * @type Number
11495      */
11496
11497     /**
11498      * @property  autoAbort
11499      * Whether a new request should abort any pending requests. (defaults to false)
11500      * @type Boolean
11501      */
11502     autoAbort : false,
11503
11504     /**
11505      * Serialize the passed form into a url encoded string
11506      * @param {String/HTMLElement} form
11507      * @return {String}
11508      */
11509     serializeForm : function(form){
11510         return Roo.lib.Ajax.serializeForm(form);
11511     }
11512 });/*
11513  * Based on:
11514  * Ext JS Library 1.1.1
11515  * Copyright(c) 2006-2007, Ext JS, LLC.
11516  *
11517  * Originally Released Under LGPL - original licence link has changed is not relivant.
11518  *
11519  * Fork - LGPL
11520  * <script type="text/javascript">
11521  */
11522  
11523 /**
11524  * @class Roo.Ajax
11525  * @extends Roo.data.Connection
11526  * Global Ajax request class.
11527  *
11528  * @instanceOf  Roo.data.Connection
11529  */
11530 Roo.Ajax = new Roo.data.Connection({
11531     // fix up the docs
11532     
11533     /**
11534      * fix up scoping
11535      * @scope Roo.Ajax
11536      */
11537     
11538    /**
11539      * @cfg {String} url @hide
11540      */
11541     /**
11542      * @cfg {Object} extraParams @hide
11543      */
11544     /**
11545      * @cfg {Object} defaultHeaders @hide
11546      */
11547     /**
11548      * @cfg {String} method (Optional) @hide
11549      */
11550     /**
11551      * @cfg {Number} timeout (Optional) @hide
11552      */
11553     /**
11554      * @cfg {Boolean} autoAbort (Optional) @hide
11555      */
11556
11557     /**
11558      * @cfg {Boolean} disableCaching (Optional) @hide
11559      */
11560
11561     /**
11562      * @property  disableCaching
11563      * True to add a unique cache-buster param to GET requests. (defaults to true)
11564      * @type Boolean
11565      */
11566     /**
11567      * @property  url
11568      * The default URL to be used for requests to the server. (defaults to undefined)
11569      * @type String
11570      */
11571     /**
11572      * @property  extraParams
11573      * An object containing properties which are used as
11574      * extra parameters to each request made by this object. (defaults to undefined)
11575      * @type Object
11576      */
11577     /**
11578      * @property  defaultHeaders
11579      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11580      * @type Object
11581      */
11582     /**
11583      * @property  method
11584      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11585      * @type String
11586      */
11587     /**
11588      * @property  timeout
11589      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11590      * @type Number
11591      */
11592
11593     /**
11594      * @property  autoAbort
11595      * Whether a new request should abort any pending requests. (defaults to false)
11596      * @type Boolean
11597      */
11598     autoAbort : false,
11599
11600     /**
11601      * Serialize the passed form into a url encoded string
11602      * @param {String/HTMLElement} form
11603      * @return {String}
11604      */
11605     serializeForm : function(form){
11606         return Roo.lib.Ajax.serializeForm(form);
11607     }
11608 });/*
11609  * Based on:
11610  * Ext JS Library 1.1.1
11611  * Copyright(c) 2006-2007, Ext JS, LLC.
11612  *
11613  * Originally Released Under LGPL - original licence link has changed is not relivant.
11614  *
11615  * Fork - LGPL
11616  * <script type="text/javascript">
11617  */
11618
11619  
11620 /**
11621  * @class Roo.UpdateManager
11622  * @extends Roo.util.Observable
11623  * Provides AJAX-style update for Element object.<br><br>
11624  * Usage:<br>
11625  * <pre><code>
11626  * // Get it from a Roo.Element object
11627  * var el = Roo.get("foo");
11628  * var mgr = el.getUpdateManager();
11629  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11630  * ...
11631  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11632  * <br>
11633  * // or directly (returns the same UpdateManager instance)
11634  * var mgr = new Roo.UpdateManager("myElementId");
11635  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11636  * mgr.on("update", myFcnNeedsToKnow);
11637  * <br>
11638    // short handed call directly from the element object
11639    Roo.get("foo").load({
11640         url: "bar.php",
11641         scripts:true,
11642         params: "for=bar",
11643         text: "Loading Foo..."
11644    });
11645  * </code></pre>
11646  * @constructor
11647  * Create new UpdateManager directly.
11648  * @param {String/HTMLElement/Roo.Element} el The element to update
11649  * @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).
11650  */
11651 Roo.UpdateManager = function(el, forceNew){
11652     el = Roo.get(el);
11653     if(!forceNew && el.updateManager){
11654         return el.updateManager;
11655     }
11656     /**
11657      * The Element object
11658      * @type Roo.Element
11659      */
11660     this.el = el;
11661     /**
11662      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11663      * @type String
11664      */
11665     this.defaultUrl = null;
11666
11667     this.addEvents({
11668         /**
11669          * @event beforeupdate
11670          * Fired before an update is made, return false from your handler and the update is cancelled.
11671          * @param {Roo.Element} el
11672          * @param {String/Object/Function} url
11673          * @param {String/Object} params
11674          */
11675         "beforeupdate": true,
11676         /**
11677          * @event update
11678          * Fired after successful update is made.
11679          * @param {Roo.Element} el
11680          * @param {Object} oResponseObject The response Object
11681          */
11682         "update": true,
11683         /**
11684          * @event failure
11685          * Fired on update failure.
11686          * @param {Roo.Element} el
11687          * @param {Object} oResponseObject The response Object
11688          */
11689         "failure": true
11690     });
11691     var d = Roo.UpdateManager.defaults;
11692     /**
11693      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11694      * @type String
11695      */
11696     this.sslBlankUrl = d.sslBlankUrl;
11697     /**
11698      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11699      * @type Boolean
11700      */
11701     this.disableCaching = d.disableCaching;
11702     /**
11703      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11704      * @type String
11705      */
11706     this.indicatorText = d.indicatorText;
11707     /**
11708      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11709      * @type String
11710      */
11711     this.showLoadIndicator = d.showLoadIndicator;
11712     /**
11713      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11714      * @type Number
11715      */
11716     this.timeout = d.timeout;
11717
11718     /**
11719      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11720      * @type Boolean
11721      */
11722     this.loadScripts = d.loadScripts;
11723
11724     /**
11725      * Transaction object of current executing transaction
11726      */
11727     this.transaction = null;
11728
11729     /**
11730      * @private
11731      */
11732     this.autoRefreshProcId = null;
11733     /**
11734      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11735      * @type Function
11736      */
11737     this.refreshDelegate = this.refresh.createDelegate(this);
11738     /**
11739      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11740      * @type Function
11741      */
11742     this.updateDelegate = this.update.createDelegate(this);
11743     /**
11744      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11745      * @type Function
11746      */
11747     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11748     /**
11749      * @private
11750      */
11751     this.successDelegate = this.processSuccess.createDelegate(this);
11752     /**
11753      * @private
11754      */
11755     this.failureDelegate = this.processFailure.createDelegate(this);
11756
11757     if(!this.renderer){
11758      /**
11759       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11760       */
11761     this.renderer = new Roo.UpdateManager.BasicRenderer();
11762     }
11763     
11764     Roo.UpdateManager.superclass.constructor.call(this);
11765 };
11766
11767 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11768     /**
11769      * Get the Element this UpdateManager is bound to
11770      * @return {Roo.Element} The element
11771      */
11772     getEl : function(){
11773         return this.el;
11774     },
11775     /**
11776      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11777      * @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:
11778 <pre><code>
11779 um.update({<br/>
11780     url: "your-url.php",<br/>
11781     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11782     callback: yourFunction,<br/>
11783     scope: yourObject, //(optional scope)  <br/>
11784     discardUrl: false, <br/>
11785     nocache: false,<br/>
11786     text: "Loading...",<br/>
11787     timeout: 30,<br/>
11788     scripts: false<br/>
11789 });
11790 </code></pre>
11791      * The only required property is url. The optional properties nocache, text and scripts
11792      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11793      * @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}
11794      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11795      * @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.
11796      */
11797     update : function(url, params, callback, discardUrl){
11798         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11799             var method = this.method, cfg;
11800             if(typeof url == "object"){ // must be config object
11801                 cfg = url;
11802                 url = cfg.url;
11803                 params = params || cfg.params;
11804                 callback = callback || cfg.callback;
11805                 discardUrl = discardUrl || cfg.discardUrl;
11806                 if(callback && cfg.scope){
11807                     callback = callback.createDelegate(cfg.scope);
11808                 }
11809                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11810                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11811                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11812                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11813                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11814             }
11815             this.showLoading();
11816             if(!discardUrl){
11817                 this.defaultUrl = url;
11818             }
11819             if(typeof url == "function"){
11820                 url = url.call(this);
11821             }
11822
11823             method = method || (params ? "POST" : "GET");
11824             if(method == "GET"){
11825                 url = this.prepareUrl(url);
11826             }
11827
11828             var o = Roo.apply(cfg ||{}, {
11829                 url : url,
11830                 params: params,
11831                 success: this.successDelegate,
11832                 failure: this.failureDelegate,
11833                 callback: undefined,
11834                 timeout: (this.timeout*1000),
11835                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11836             });
11837
11838             this.transaction = Roo.Ajax.request(o);
11839         }
11840     },
11841
11842     /**
11843      * 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.
11844      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11845      * @param {String/HTMLElement} form The form Id or form element
11846      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11847      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11848      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11849      */
11850     formUpdate : function(form, url, reset, callback){
11851         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11852             if(typeof url == "function"){
11853                 url = url.call(this);
11854             }
11855             form = Roo.getDom(form);
11856             this.transaction = Roo.Ajax.request({
11857                 form: form,
11858                 url:url,
11859                 success: this.successDelegate,
11860                 failure: this.failureDelegate,
11861                 timeout: (this.timeout*1000),
11862                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11863             });
11864             this.showLoading.defer(1, this);
11865         }
11866     },
11867
11868     /**
11869      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11870      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11871      */
11872     refresh : function(callback){
11873         if(this.defaultUrl == null){
11874             return;
11875         }
11876         this.update(this.defaultUrl, null, callback, true);
11877     },
11878
11879     /**
11880      * Set this element to auto refresh.
11881      * @param {Number} interval How often to update (in seconds).
11882      * @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)
11883      * @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}
11884      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11885      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11886      */
11887     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11888         if(refreshNow){
11889             this.update(url || this.defaultUrl, params, callback, true);
11890         }
11891         if(this.autoRefreshProcId){
11892             clearInterval(this.autoRefreshProcId);
11893         }
11894         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11895     },
11896
11897     /**
11898      * Stop auto refresh on this element.
11899      */
11900      stopAutoRefresh : function(){
11901         if(this.autoRefreshProcId){
11902             clearInterval(this.autoRefreshProcId);
11903             delete this.autoRefreshProcId;
11904         }
11905     },
11906
11907     isAutoRefreshing : function(){
11908        return this.autoRefreshProcId ? true : false;
11909     },
11910     /**
11911      * Called to update the element to "Loading" state. Override to perform custom action.
11912      */
11913     showLoading : function(){
11914         if(this.showLoadIndicator){
11915             this.el.update(this.indicatorText);
11916         }
11917     },
11918
11919     /**
11920      * Adds unique parameter to query string if disableCaching = true
11921      * @private
11922      */
11923     prepareUrl : function(url){
11924         if(this.disableCaching){
11925             var append = "_dc=" + (new Date().getTime());
11926             if(url.indexOf("?") !== -1){
11927                 url += "&" + append;
11928             }else{
11929                 url += "?" + append;
11930             }
11931         }
11932         return url;
11933     },
11934
11935     /**
11936      * @private
11937      */
11938     processSuccess : function(response){
11939         this.transaction = null;
11940         if(response.argument.form && response.argument.reset){
11941             try{ // put in try/catch since some older FF releases had problems with this
11942                 response.argument.form.reset();
11943             }catch(e){}
11944         }
11945         if(this.loadScripts){
11946             this.renderer.render(this.el, response, this,
11947                 this.updateComplete.createDelegate(this, [response]));
11948         }else{
11949             this.renderer.render(this.el, response, this);
11950             this.updateComplete(response);
11951         }
11952     },
11953
11954     updateComplete : function(response){
11955         this.fireEvent("update", this.el, response);
11956         if(typeof response.argument.callback == "function"){
11957             response.argument.callback(this.el, true, response);
11958         }
11959     },
11960
11961     /**
11962      * @private
11963      */
11964     processFailure : function(response){
11965         this.transaction = null;
11966         this.fireEvent("failure", this.el, response);
11967         if(typeof response.argument.callback == "function"){
11968             response.argument.callback(this.el, false, response);
11969         }
11970     },
11971
11972     /**
11973      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11974      * @param {Object} renderer The object implementing the render() method
11975      */
11976     setRenderer : function(renderer){
11977         this.renderer = renderer;
11978     },
11979
11980     getRenderer : function(){
11981        return this.renderer;
11982     },
11983
11984     /**
11985      * Set the defaultUrl used for updates
11986      * @param {String/Function} defaultUrl The url or a function to call to get the url
11987      */
11988     setDefaultUrl : function(defaultUrl){
11989         this.defaultUrl = defaultUrl;
11990     },
11991
11992     /**
11993      * Aborts the executing transaction
11994      */
11995     abort : function(){
11996         if(this.transaction){
11997             Roo.Ajax.abort(this.transaction);
11998         }
11999     },
12000
12001     /**
12002      * Returns true if an update is in progress
12003      * @return {Boolean}
12004      */
12005     isUpdating : function(){
12006         if(this.transaction){
12007             return Roo.Ajax.isLoading(this.transaction);
12008         }
12009         return false;
12010     }
12011 });
12012
12013 /**
12014  * @class Roo.UpdateManager.defaults
12015  * @static (not really - but it helps the doc tool)
12016  * The defaults collection enables customizing the default properties of UpdateManager
12017  */
12018    Roo.UpdateManager.defaults = {
12019        /**
12020          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12021          * @type Number
12022          */
12023          timeout : 30,
12024
12025          /**
12026          * True to process scripts by default (Defaults to false).
12027          * @type Boolean
12028          */
12029         loadScripts : false,
12030
12031         /**
12032         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12033         * @type String
12034         */
12035         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12036         /**
12037          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12038          * @type Boolean
12039          */
12040         disableCaching : false,
12041         /**
12042          * Whether to show indicatorText when loading (Defaults to true).
12043          * @type Boolean
12044          */
12045         showLoadIndicator : true,
12046         /**
12047          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12048          * @type String
12049          */
12050         indicatorText : '<div class="loading-indicator">Loading...</div>'
12051    };
12052
12053 /**
12054  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12055  *Usage:
12056  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12057  * @param {String/HTMLElement/Roo.Element} el The element to update
12058  * @param {String} url The url
12059  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12060  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12061  * @static
12062  * @deprecated
12063  * @member Roo.UpdateManager
12064  */
12065 Roo.UpdateManager.updateElement = function(el, url, params, options){
12066     var um = Roo.get(el, true).getUpdateManager();
12067     Roo.apply(um, options);
12068     um.update(url, params, options ? options.callback : null);
12069 };
12070 // alias for backwards compat
12071 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12072 /**
12073  * @class Roo.UpdateManager.BasicRenderer
12074  * Default Content renderer. Updates the elements innerHTML with the responseText.
12075  */
12076 Roo.UpdateManager.BasicRenderer = function(){};
12077
12078 Roo.UpdateManager.BasicRenderer.prototype = {
12079     /**
12080      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12081      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12082      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12083      * @param {Roo.Element} el The element being rendered
12084      * @param {Object} response The YUI Connect response object
12085      * @param {UpdateManager} updateManager The calling update manager
12086      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12087      */
12088      render : function(el, response, updateManager, callback){
12089         el.update(response.responseText, updateManager.loadScripts, callback);
12090     }
12091 };
12092 /*
12093  * Based on:
12094  * Ext JS Library 1.1.1
12095  * Copyright(c) 2006-2007, Ext JS, LLC.
12096  *
12097  * Originally Released Under LGPL - original licence link has changed is not relivant.
12098  *
12099  * Fork - LGPL
12100  * <script type="text/javascript">
12101  */
12102
12103 /**
12104  * @class Roo.util.DelayedTask
12105  * Provides a convenient method of performing setTimeout where a new
12106  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12107  * You can use this class to buffer
12108  * the keypress events for a certain number of milliseconds, and perform only if they stop
12109  * for that amount of time.
12110  * @constructor The parameters to this constructor serve as defaults and are not required.
12111  * @param {Function} fn (optional) The default function to timeout
12112  * @param {Object} scope (optional) The default scope of that timeout
12113  * @param {Array} args (optional) The default Array of arguments
12114  */
12115 Roo.util.DelayedTask = function(fn, scope, args){
12116     var id = null, d, t;
12117
12118     var call = function(){
12119         var now = new Date().getTime();
12120         if(now - t >= d){
12121             clearInterval(id);
12122             id = null;
12123             fn.apply(scope, args || []);
12124         }
12125     };
12126     /**
12127      * Cancels any pending timeout and queues a new one
12128      * @param {Number} delay The milliseconds to delay
12129      * @param {Function} newFn (optional) Overrides function passed to constructor
12130      * @param {Object} newScope (optional) Overrides scope passed to constructor
12131      * @param {Array} newArgs (optional) Overrides args passed to constructor
12132      */
12133     this.delay = function(delay, newFn, newScope, newArgs){
12134         if(id && delay != d){
12135             this.cancel();
12136         }
12137         d = delay;
12138         t = new Date().getTime();
12139         fn = newFn || fn;
12140         scope = newScope || scope;
12141         args = newArgs || args;
12142         if(!id){
12143             id = setInterval(call, d);
12144         }
12145     };
12146
12147     /**
12148      * Cancel the last queued timeout
12149      */
12150     this.cancel = function(){
12151         if(id){
12152             clearInterval(id);
12153             id = null;
12154         }
12155     };
12156 };/*
12157  * Based on:
12158  * Ext JS Library 1.1.1
12159  * Copyright(c) 2006-2007, Ext JS, LLC.
12160  *
12161  * Originally Released Under LGPL - original licence link has changed is not relivant.
12162  *
12163  * Fork - LGPL
12164  * <script type="text/javascript">
12165  */
12166  
12167  
12168 Roo.util.TaskRunner = function(interval){
12169     interval = interval || 10;
12170     var tasks = [], removeQueue = [];
12171     var id = 0;
12172     var running = false;
12173
12174     var stopThread = function(){
12175         running = false;
12176         clearInterval(id);
12177         id = 0;
12178     };
12179
12180     var startThread = function(){
12181         if(!running){
12182             running = true;
12183             id = setInterval(runTasks, interval);
12184         }
12185     };
12186
12187     var removeTask = function(task){
12188         removeQueue.push(task);
12189         if(task.onStop){
12190             task.onStop();
12191         }
12192     };
12193
12194     var runTasks = function(){
12195         if(removeQueue.length > 0){
12196             for(var i = 0, len = removeQueue.length; i < len; i++){
12197                 tasks.remove(removeQueue[i]);
12198             }
12199             removeQueue = [];
12200             if(tasks.length < 1){
12201                 stopThread();
12202                 return;
12203             }
12204         }
12205         var now = new Date().getTime();
12206         for(var i = 0, len = tasks.length; i < len; ++i){
12207             var t = tasks[i];
12208             var itime = now - t.taskRunTime;
12209             if(t.interval <= itime){
12210                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12211                 t.taskRunTime = now;
12212                 if(rt === false || t.taskRunCount === t.repeat){
12213                     removeTask(t);
12214                     return;
12215                 }
12216             }
12217             if(t.duration && t.duration <= (now - t.taskStartTime)){
12218                 removeTask(t);
12219             }
12220         }
12221     };
12222
12223     /**
12224      * Queues a new task.
12225      * @param {Object} task
12226      */
12227     this.start = function(task){
12228         tasks.push(task);
12229         task.taskStartTime = new Date().getTime();
12230         task.taskRunTime = 0;
12231         task.taskRunCount = 0;
12232         startThread();
12233         return task;
12234     };
12235
12236     this.stop = function(task){
12237         removeTask(task);
12238         return task;
12239     };
12240
12241     this.stopAll = function(){
12242         stopThread();
12243         for(var i = 0, len = tasks.length; i < len; i++){
12244             if(tasks[i].onStop){
12245                 tasks[i].onStop();
12246             }
12247         }
12248         tasks = [];
12249         removeQueue = [];
12250     };
12251 };
12252
12253 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12254  * Based on:
12255  * Ext JS Library 1.1.1
12256  * Copyright(c) 2006-2007, Ext JS, LLC.
12257  *
12258  * Originally Released Under LGPL - original licence link has changed is not relivant.
12259  *
12260  * Fork - LGPL
12261  * <script type="text/javascript">
12262  */
12263
12264  
12265 /**
12266  * @class Roo.util.MixedCollection
12267  * @extends Roo.util.Observable
12268  * A Collection class that maintains both numeric indexes and keys and exposes events.
12269  * @constructor
12270  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12271  * collection (defaults to false)
12272  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12273  * and return the key value for that item.  This is used when available to look up the key on items that
12274  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12275  * equivalent to providing an implementation for the {@link #getKey} method.
12276  */
12277 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12278     this.items = [];
12279     this.map = {};
12280     this.keys = [];
12281     this.length = 0;
12282     this.addEvents({
12283         /**
12284          * @event clear
12285          * Fires when the collection is cleared.
12286          */
12287         "clear" : true,
12288         /**
12289          * @event add
12290          * Fires when an item is added to the collection.
12291          * @param {Number} index The index at which the item was added.
12292          * @param {Object} o The item added.
12293          * @param {String} key The key associated with the added item.
12294          */
12295         "add" : true,
12296         /**
12297          * @event replace
12298          * Fires when an item is replaced in the collection.
12299          * @param {String} key he key associated with the new added.
12300          * @param {Object} old The item being replaced.
12301          * @param {Object} new The new item.
12302          */
12303         "replace" : true,
12304         /**
12305          * @event remove
12306          * Fires when an item is removed from the collection.
12307          * @param {Object} o The item being removed.
12308          * @param {String} key (optional) The key associated with the removed item.
12309          */
12310         "remove" : true,
12311         "sort" : true
12312     });
12313     this.allowFunctions = allowFunctions === true;
12314     if(keyFn){
12315         this.getKey = keyFn;
12316     }
12317     Roo.util.MixedCollection.superclass.constructor.call(this);
12318 };
12319
12320 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12321     allowFunctions : false,
12322     
12323 /**
12324  * Adds an item to the collection.
12325  * @param {String} key The key to associate with the item
12326  * @param {Object} o The item to add.
12327  * @return {Object} The item added.
12328  */
12329     add : function(key, o){
12330         if(arguments.length == 1){
12331             o = arguments[0];
12332             key = this.getKey(o);
12333         }
12334         if(typeof key == "undefined" || key === null){
12335             this.length++;
12336             this.items.push(o);
12337             this.keys.push(null);
12338         }else{
12339             var old = this.map[key];
12340             if(old){
12341                 return this.replace(key, o);
12342             }
12343             this.length++;
12344             this.items.push(o);
12345             this.map[key] = o;
12346             this.keys.push(key);
12347         }
12348         this.fireEvent("add", this.length-1, o, key);
12349         return o;
12350     },
12351        
12352 /**
12353   * MixedCollection has a generic way to fetch keys if you implement getKey.
12354 <pre><code>
12355 // normal way
12356 var mc = new Roo.util.MixedCollection();
12357 mc.add(someEl.dom.id, someEl);
12358 mc.add(otherEl.dom.id, otherEl);
12359 //and so on
12360
12361 // using getKey
12362 var mc = new Roo.util.MixedCollection();
12363 mc.getKey = function(el){
12364    return el.dom.id;
12365 };
12366 mc.add(someEl);
12367 mc.add(otherEl);
12368
12369 // or via the constructor
12370 var mc = new Roo.util.MixedCollection(false, function(el){
12371    return el.dom.id;
12372 });
12373 mc.add(someEl);
12374 mc.add(otherEl);
12375 </code></pre>
12376  * @param o {Object} The item for which to find the key.
12377  * @return {Object} The key for the passed item.
12378  */
12379     getKey : function(o){
12380          return o.id; 
12381     },
12382    
12383 /**
12384  * Replaces an item in the collection.
12385  * @param {String} key The key associated with the item to replace, or the item to replace.
12386  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12387  * @return {Object}  The new item.
12388  */
12389     replace : function(key, o){
12390         if(arguments.length == 1){
12391             o = arguments[0];
12392             key = this.getKey(o);
12393         }
12394         var old = this.item(key);
12395         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12396              return this.add(key, o);
12397         }
12398         var index = this.indexOfKey(key);
12399         this.items[index] = o;
12400         this.map[key] = o;
12401         this.fireEvent("replace", key, old, o);
12402         return o;
12403     },
12404    
12405 /**
12406  * Adds all elements of an Array or an Object to the collection.
12407  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12408  * an Array of values, each of which are added to the collection.
12409  */
12410     addAll : function(objs){
12411         if(arguments.length > 1 || objs instanceof Array){
12412             var args = arguments.length > 1 ? arguments : objs;
12413             for(var i = 0, len = args.length; i < len; i++){
12414                 this.add(args[i]);
12415             }
12416         }else{
12417             for(var key in objs){
12418                 if(this.allowFunctions || typeof objs[key] != "function"){
12419                     this.add(key, objs[key]);
12420                 }
12421             }
12422         }
12423     },
12424    
12425 /**
12426  * Executes the specified function once for every item in the collection, passing each
12427  * item as the first and only parameter. returning false from the function will stop the iteration.
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     each : function(fn, scope){
12432         var items = [].concat(this.items); // each safe for removal
12433         for(var i = 0, len = items.length; i < len; i++){
12434             if(fn.call(scope || items[i], items[i], i, len) === false){
12435                 break;
12436             }
12437         }
12438     },
12439    
12440 /**
12441  * Executes the specified function once for every key in the collection, passing each
12442  * key, and its associated item as the first two parameters.
12443  * @param {Function} fn The function to execute for each item.
12444  * @param {Object} scope (optional) The scope in which to execute the function.
12445  */
12446     eachKey : function(fn, scope){
12447         for(var i = 0, len = this.keys.length; i < len; i++){
12448             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12449         }
12450     },
12451    
12452 /**
12453  * Returns the first item in the collection which elicits a true return value from the
12454  * passed selection function.
12455  * @param {Function} fn The selection function to execute for each item.
12456  * @param {Object} scope (optional) The scope in which to execute the function.
12457  * @return {Object} The first item in the collection which returned true from the selection function.
12458  */
12459     find : function(fn, scope){
12460         for(var i = 0, len = this.items.length; i < len; i++){
12461             if(fn.call(scope || window, this.items[i], this.keys[i])){
12462                 return this.items[i];
12463             }
12464         }
12465         return null;
12466     },
12467    
12468 /**
12469  * Inserts an item at the specified index in the collection.
12470  * @param {Number} index The index to insert the item at.
12471  * @param {String} key The key to associate with the new item, or the item itself.
12472  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12473  * @return {Object} The item inserted.
12474  */
12475     insert : function(index, key, o){
12476         if(arguments.length == 2){
12477             o = arguments[1];
12478             key = this.getKey(o);
12479         }
12480         if(index >= this.length){
12481             return this.add(key, o);
12482         }
12483         this.length++;
12484         this.items.splice(index, 0, o);
12485         if(typeof key != "undefined" && key != null){
12486             this.map[key] = o;
12487         }
12488         this.keys.splice(index, 0, key);
12489         this.fireEvent("add", index, o, key);
12490         return o;
12491     },
12492    
12493 /**
12494  * Removed an item from the collection.
12495  * @param {Object} o The item to remove.
12496  * @return {Object} The item removed.
12497  */
12498     remove : function(o){
12499         return this.removeAt(this.indexOf(o));
12500     },
12501    
12502 /**
12503  * Remove an item from a specified index in the collection.
12504  * @param {Number} index The index within the collection of the item to remove.
12505  */
12506     removeAt : function(index){
12507         if(index < this.length && index >= 0){
12508             this.length--;
12509             var o = this.items[index];
12510             this.items.splice(index, 1);
12511             var key = this.keys[index];
12512             if(typeof key != "undefined"){
12513                 delete this.map[key];
12514             }
12515             this.keys.splice(index, 1);
12516             this.fireEvent("remove", o, key);
12517         }
12518     },
12519    
12520 /**
12521  * Removed an item associated with the passed key fom the collection.
12522  * @param {String} key The key of the item to remove.
12523  */
12524     removeKey : function(key){
12525         return this.removeAt(this.indexOfKey(key));
12526     },
12527    
12528 /**
12529  * Returns the number of items in the collection.
12530  * @return {Number} the number of items in the collection.
12531  */
12532     getCount : function(){
12533         return this.length; 
12534     },
12535    
12536 /**
12537  * Returns index within the collection of the passed Object.
12538  * @param {Object} o The item to find the index of.
12539  * @return {Number} index of the item.
12540  */
12541     indexOf : function(o){
12542         if(!this.items.indexOf){
12543             for(var i = 0, len = this.items.length; i < len; i++){
12544                 if(this.items[i] == o) return i;
12545             }
12546             return -1;
12547         }else{
12548             return this.items.indexOf(o);
12549         }
12550     },
12551    
12552 /**
12553  * Returns index within the collection of the passed key.
12554  * @param {String} key The key to find the index of.
12555  * @return {Number} index of the key.
12556  */
12557     indexOfKey : function(key){
12558         if(!this.keys.indexOf){
12559             for(var i = 0, len = this.keys.length; i < len; i++){
12560                 if(this.keys[i] == key) return i;
12561             }
12562             return -1;
12563         }else{
12564             return this.keys.indexOf(key);
12565         }
12566     },
12567    
12568 /**
12569  * Returns the item associated with the passed key OR index. Key has priority over index.
12570  * @param {String/Number} key The key or index of the item.
12571  * @return {Object} The item associated with the passed key.
12572  */
12573     item : function(key){
12574         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12575         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12576     },
12577     
12578 /**
12579  * Returns the item at the specified index.
12580  * @param {Number} index The index of the item.
12581  * @return {Object}
12582  */
12583     itemAt : function(index){
12584         return this.items[index];
12585     },
12586     
12587 /**
12588  * Returns the item associated with the passed key.
12589  * @param {String/Number} key The key of the item.
12590  * @return {Object} The item associated with the passed key.
12591  */
12592     key : function(key){
12593         return this.map[key];
12594     },
12595    
12596 /**
12597  * Returns true if the collection contains the passed Object as an item.
12598  * @param {Object} o  The Object to look for in the collection.
12599  * @return {Boolean} True if the collection contains the Object as an item.
12600  */
12601     contains : function(o){
12602         return this.indexOf(o) != -1;
12603     },
12604    
12605 /**
12606  * Returns true if the collection contains the passed Object as a key.
12607  * @param {String} key The key to look for in the collection.
12608  * @return {Boolean} True if the collection contains the Object as a key.
12609  */
12610     containsKey : function(key){
12611         return typeof this.map[key] != "undefined";
12612     },
12613    
12614 /**
12615  * Removes all items from the collection.
12616  */
12617     clear : function(){
12618         this.length = 0;
12619         this.items = [];
12620         this.keys = [];
12621         this.map = {};
12622         this.fireEvent("clear");
12623     },
12624    
12625 /**
12626  * Returns the first item in the collection.
12627  * @return {Object} the first item in the collection..
12628  */
12629     first : function(){
12630         return this.items[0]; 
12631     },
12632    
12633 /**
12634  * Returns the last item in the collection.
12635  * @return {Object} the last item in the collection..
12636  */
12637     last : function(){
12638         return this.items[this.length-1];   
12639     },
12640     
12641     _sort : function(property, dir, fn){
12642         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12643         fn = fn || function(a, b){
12644             return a-b;
12645         };
12646         var c = [], k = this.keys, items = this.items;
12647         for(var i = 0, len = items.length; i < len; i++){
12648             c[c.length] = {key: k[i], value: items[i], index: i};
12649         }
12650         c.sort(function(a, b){
12651             var v = fn(a[property], b[property]) * dsc;
12652             if(v == 0){
12653                 v = (a.index < b.index ? -1 : 1);
12654             }
12655             return v;
12656         });
12657         for(var i = 0, len = c.length; i < len; i++){
12658             items[i] = c[i].value;
12659             k[i] = c[i].key;
12660         }
12661         this.fireEvent("sort", this);
12662     },
12663     
12664     /**
12665      * Sorts this collection with the passed comparison function
12666      * @param {String} direction (optional) "ASC" or "DESC"
12667      * @param {Function} fn (optional) comparison function
12668      */
12669     sort : function(dir, fn){
12670         this._sort("value", dir, fn);
12671     },
12672     
12673     /**
12674      * Sorts this collection by keys
12675      * @param {String} direction (optional) "ASC" or "DESC"
12676      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12677      */
12678     keySort : function(dir, fn){
12679         this._sort("key", dir, fn || function(a, b){
12680             return String(a).toUpperCase()-String(b).toUpperCase();
12681         });
12682     },
12683     
12684     /**
12685      * Returns a range of items in this collection
12686      * @param {Number} startIndex (optional) defaults to 0
12687      * @param {Number} endIndex (optional) default to the last item
12688      * @return {Array} An array of items
12689      */
12690     getRange : function(start, end){
12691         var items = this.items;
12692         if(items.length < 1){
12693             return [];
12694         }
12695         start = start || 0;
12696         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12697         var r = [];
12698         if(start <= end){
12699             for(var i = start; i <= end; i++) {
12700                     r[r.length] = items[i];
12701             }
12702         }else{
12703             for(var i = start; i >= end; i--) {
12704                     r[r.length] = items[i];
12705             }
12706         }
12707         return r;
12708     },
12709         
12710     /**
12711      * Filter the <i>objects</i> in this collection by a specific property. 
12712      * Returns a new collection that has been filtered.
12713      * @param {String} property A property on your objects
12714      * @param {String/RegExp} value Either string that the property values 
12715      * should start with or a RegExp to test against the property
12716      * @return {MixedCollection} The new filtered collection
12717      */
12718     filter : function(property, value){
12719         if(!value.exec){ // not a regex
12720             value = String(value);
12721             if(value.length == 0){
12722                 return this.clone();
12723             }
12724             value = new RegExp("^" + Roo.escapeRe(value), "i");
12725         }
12726         return this.filterBy(function(o){
12727             return o && value.test(o[property]);
12728         });
12729         },
12730     
12731     /**
12732      * Filter by a function. * Returns a new collection that has been filtered.
12733      * The passed function will be called with each 
12734      * object in the collection. If the function returns true, the value is included 
12735      * otherwise it is filtered.
12736      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12737      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12738      * @return {MixedCollection} The new filtered collection
12739      */
12740     filterBy : function(fn, scope){
12741         var r = new Roo.util.MixedCollection();
12742         r.getKey = this.getKey;
12743         var k = this.keys, it = this.items;
12744         for(var i = 0, len = it.length; i < len; i++){
12745             if(fn.call(scope||this, it[i], k[i])){
12746                                 r.add(k[i], it[i]);
12747                         }
12748         }
12749         return r;
12750     },
12751     
12752     /**
12753      * Creates a duplicate of this collection
12754      * @return {MixedCollection}
12755      */
12756     clone : function(){
12757         var r = new Roo.util.MixedCollection();
12758         var k = this.keys, it = this.items;
12759         for(var i = 0, len = it.length; i < len; i++){
12760             r.add(k[i], it[i]);
12761         }
12762         r.getKey = this.getKey;
12763         return r;
12764     }
12765 });
12766 /**
12767  * Returns the item associated with the passed key or index.
12768  * @method
12769  * @param {String/Number} key The key or index of the item.
12770  * @return {Object} The item associated with the passed key.
12771  */
12772 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12773  * Based on:
12774  * Ext JS Library 1.1.1
12775  * Copyright(c) 2006-2007, Ext JS, LLC.
12776  *
12777  * Originally Released Under LGPL - original licence link has changed is not relivant.
12778  *
12779  * Fork - LGPL
12780  * <script type="text/javascript">
12781  */
12782 /**
12783  * @class Roo.util.JSON
12784  * Modified version of Douglas Crockford"s json.js that doesn"t
12785  * mess with the Object prototype 
12786  * http://www.json.org/js.html
12787  * @singleton
12788  */
12789 Roo.util.JSON = new (function(){
12790     var useHasOwn = {}.hasOwnProperty ? true : false;
12791     
12792     // crashes Safari in some instances
12793     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12794     
12795     var pad = function(n) {
12796         return n < 10 ? "0" + n : n;
12797     };
12798     
12799     var m = {
12800         "\b": '\\b',
12801         "\t": '\\t',
12802         "\n": '\\n',
12803         "\f": '\\f',
12804         "\r": '\\r',
12805         '"' : '\\"',
12806         "\\": '\\\\'
12807     };
12808
12809     var encodeString = function(s){
12810         if (/["\\\x00-\x1f]/.test(s)) {
12811             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12812                 var c = m[b];
12813                 if(c){
12814                     return c;
12815                 }
12816                 c = b.charCodeAt();
12817                 return "\\u00" +
12818                     Math.floor(c / 16).toString(16) +
12819                     (c % 16).toString(16);
12820             }) + '"';
12821         }
12822         return '"' + s + '"';
12823     };
12824     
12825     var encodeArray = function(o){
12826         var a = ["["], b, i, l = o.length, v;
12827             for (i = 0; i < l; i += 1) {
12828                 v = o[i];
12829                 switch (typeof v) {
12830                     case "undefined":
12831                     case "function":
12832                     case "unknown":
12833                         break;
12834                     default:
12835                         if (b) {
12836                             a.push(',');
12837                         }
12838                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12839                         b = true;
12840                 }
12841             }
12842             a.push("]");
12843             return a.join("");
12844     };
12845     
12846     var encodeDate = function(o){
12847         return '"' + o.getFullYear() + "-" +
12848                 pad(o.getMonth() + 1) + "-" +
12849                 pad(o.getDate()) + "T" +
12850                 pad(o.getHours()) + ":" +
12851                 pad(o.getMinutes()) + ":" +
12852                 pad(o.getSeconds()) + '"';
12853     };
12854     
12855     /**
12856      * Encodes an Object, Array or other value
12857      * @param {Mixed} o The variable to encode
12858      * @return {String} The JSON string
12859      */
12860     this.encode = function(o)
12861     {
12862         // should this be extended to fully wrap stringify..
12863         
12864         if(typeof o == "undefined" || o === null){
12865             return "null";
12866         }else if(o instanceof Array){
12867             return encodeArray(o);
12868         }else if(o instanceof Date){
12869             return encodeDate(o);
12870         }else if(typeof o == "string"){
12871             return encodeString(o);
12872         }else if(typeof o == "number"){
12873             return isFinite(o) ? String(o) : "null";
12874         }else if(typeof o == "boolean"){
12875             return String(o);
12876         }else {
12877             var a = ["{"], b, i, v;
12878             for (i in o) {
12879                 if(!useHasOwn || o.hasOwnProperty(i)) {
12880                     v = o[i];
12881                     switch (typeof v) {
12882                     case "undefined":
12883                     case "function":
12884                     case "unknown":
12885                         break;
12886                     default:
12887                         if(b){
12888                             a.push(',');
12889                         }
12890                         a.push(this.encode(i), ":",
12891                                 v === null ? "null" : this.encode(v));
12892                         b = true;
12893                     }
12894                 }
12895             }
12896             a.push("}");
12897             return a.join("");
12898         }
12899     };
12900     
12901     /**
12902      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12903      * @param {String} json The JSON string
12904      * @return {Object} The resulting object
12905      */
12906     this.decode = function(json){
12907         
12908         return  /** eval:var:json */ eval("(" + json + ')');
12909     };
12910 })();
12911 /** 
12912  * Shorthand for {@link Roo.util.JSON#encode}
12913  * @member Roo encode 
12914  * @method */
12915 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12916 /** 
12917  * Shorthand for {@link Roo.util.JSON#decode}
12918  * @member Roo decode 
12919  * @method */
12920 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12921 /*
12922  * Based on:
12923  * Ext JS Library 1.1.1
12924  * Copyright(c) 2006-2007, Ext JS, LLC.
12925  *
12926  * Originally Released Under LGPL - original licence link has changed is not relivant.
12927  *
12928  * Fork - LGPL
12929  * <script type="text/javascript">
12930  */
12931  
12932 /**
12933  * @class Roo.util.Format
12934  * Reusable data formatting functions
12935  * @singleton
12936  */
12937 Roo.util.Format = function(){
12938     var trimRe = /^\s+|\s+$/g;
12939     return {
12940         /**
12941          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12942          * @param {String} value The string to truncate
12943          * @param {Number} length The maximum length to allow before truncating
12944          * @return {String} The converted text
12945          */
12946         ellipsis : function(value, len){
12947             if(value && value.length > len){
12948                 return value.substr(0, len-3)+"...";
12949             }
12950             return value;
12951         },
12952
12953         /**
12954          * Checks a reference and converts it to empty string if it is undefined
12955          * @param {Mixed} value Reference to check
12956          * @return {Mixed} Empty string if converted, otherwise the original value
12957          */
12958         undef : function(value){
12959             return typeof value != "undefined" ? value : "";
12960         },
12961
12962         /**
12963          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12964          * @param {String} value The string to encode
12965          * @return {String} The encoded text
12966          */
12967         htmlEncode : function(value){
12968             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12969         },
12970
12971         /**
12972          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12973          * @param {String} value The string to decode
12974          * @return {String} The decoded text
12975          */
12976         htmlDecode : function(value){
12977             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12978         },
12979
12980         /**
12981          * Trims any whitespace from either side of a string
12982          * @param {String} value The text to trim
12983          * @return {String} The trimmed text
12984          */
12985         trim : function(value){
12986             return String(value).replace(trimRe, "");
12987         },
12988
12989         /**
12990          * Returns a substring from within an original string
12991          * @param {String} value The original text
12992          * @param {Number} start The start index of the substring
12993          * @param {Number} length The length of the substring
12994          * @return {String} The substring
12995          */
12996         substr : function(value, start, length){
12997             return String(value).substr(start, length);
12998         },
12999
13000         /**
13001          * Converts a string to all lower case letters
13002          * @param {String} value The text to convert
13003          * @return {String} The converted text
13004          */
13005         lowercase : function(value){
13006             return String(value).toLowerCase();
13007         },
13008
13009         /**
13010          * Converts a string to all upper case letters
13011          * @param {String} value The text to convert
13012          * @return {String} The converted text
13013          */
13014         uppercase : function(value){
13015             return String(value).toUpperCase();
13016         },
13017
13018         /**
13019          * Converts the first character only of a string to upper case
13020          * @param {String} value The text to convert
13021          * @return {String} The converted text
13022          */
13023         capitalize : function(value){
13024             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13025         },
13026
13027         // private
13028         call : function(value, fn){
13029             if(arguments.length > 2){
13030                 var args = Array.prototype.slice.call(arguments, 2);
13031                 args.unshift(value);
13032                  
13033                 return /** eval:var:value */  eval(fn).apply(window, args);
13034             }else{
13035                 /** eval:var:value */
13036                 return /** eval:var:value */ eval(fn).call(window, value);
13037             }
13038         },
13039
13040        
13041         /**
13042          * safer version of Math.toFixed..??/
13043          * @param {Number/String} value The numeric value to format
13044          * @param {Number/String} value Decimal places 
13045          * @return {String} The formatted currency string
13046          */
13047         toFixed : function(v, n)
13048         {
13049             // why not use to fixed - precision is buggered???
13050             if (!n) {
13051                 return Math.round(v-0);
13052             }
13053             var fact = Math.pow(10,n+1);
13054             v = (Math.round((v-0)*fact))/fact;
13055             var z = (''+fact).substring(2);
13056             if (v == Math.floor(v)) {
13057                 return Math.floor(v) + '.' + z;
13058             }
13059             
13060             // now just padd decimals..
13061             var ps = String(v).split('.');
13062             var fd = (ps[1] + z);
13063             var r = fd.substring(0,n); 
13064             var rm = fd.substring(n); 
13065             if (rm < 5) {
13066                 return ps[0] + '.' + r;
13067             }
13068             r*=1; // turn it into a number;
13069             r++;
13070             if (String(r).length != n) {
13071                 ps[0]*=1;
13072                 ps[0]++;
13073                 r = String(r).substring(1); // chop the end off.
13074             }
13075             
13076             return ps[0] + '.' + r;
13077              
13078         },
13079         
13080         /**
13081          * Format a number as US currency
13082          * @param {Number/String} value The numeric value to format
13083          * @return {String} The formatted currency string
13084          */
13085         usMoney : function(v){
13086             v = (Math.round((v-0)*100))/100;
13087             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13088             v = String(v);
13089             var ps = v.split('.');
13090             var whole = ps[0];
13091             var sub = ps[1] ? '.'+ ps[1] : '.00';
13092             var r = /(\d+)(\d{3})/;
13093             while (r.test(whole)) {
13094                 whole = whole.replace(r, '$1' + ',' + '$2');
13095             }
13096             return "$" + whole + sub ;
13097         },
13098         
13099         /**
13100          * Parse a value into a formatted date using the specified format pattern.
13101          * @param {Mixed} value The value to format
13102          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13103          * @return {String} The formatted date string
13104          */
13105         date : function(v, format){
13106             if(!v){
13107                 return "";
13108             }
13109             if(!(v instanceof Date)){
13110                 v = new Date(Date.parse(v));
13111             }
13112             return v.dateFormat(format || "m/d/Y");
13113         },
13114
13115         /**
13116          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13117          * @param {String} format Any valid date format string
13118          * @return {Function} The date formatting function
13119          */
13120         dateRenderer : function(format){
13121             return function(v){
13122                 return Roo.util.Format.date(v, format);  
13123             };
13124         },
13125
13126         // private
13127         stripTagsRE : /<\/?[^>]+>/gi,
13128         
13129         /**
13130          * Strips all HTML tags
13131          * @param {Mixed} value The text from which to strip tags
13132          * @return {String} The stripped text
13133          */
13134         stripTags : function(v){
13135             return !v ? v : String(v).replace(this.stripTagsRE, "");
13136         }
13137     };
13138 }();/*
13139  * Based on:
13140  * Ext JS Library 1.1.1
13141  * Copyright(c) 2006-2007, Ext JS, LLC.
13142  *
13143  * Originally Released Under LGPL - original licence link has changed is not relivant.
13144  *
13145  * Fork - LGPL
13146  * <script type="text/javascript">
13147  */
13148
13149
13150  
13151
13152 /**
13153  * @class Roo.MasterTemplate
13154  * @extends Roo.Template
13155  * Provides a template that can have child templates. The syntax is:
13156 <pre><code>
13157 var t = new Roo.MasterTemplate(
13158         '&lt;select name="{name}"&gt;',
13159                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13160         '&lt;/select&gt;'
13161 );
13162 t.add('options', {value: 'foo', text: 'bar'});
13163 // or you can add multiple child elements in one shot
13164 t.addAll('options', [
13165     {value: 'foo', text: 'bar'},
13166     {value: 'foo2', text: 'bar2'},
13167     {value: 'foo3', text: 'bar3'}
13168 ]);
13169 // then append, applying the master template values
13170 t.append('my-form', {name: 'my-select'});
13171 </code></pre>
13172 * A name attribute for the child template is not required if you have only one child
13173 * template or you want to refer to them by index.
13174  */
13175 Roo.MasterTemplate = function(){
13176     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13177     this.originalHtml = this.html;
13178     var st = {};
13179     var m, re = this.subTemplateRe;
13180     re.lastIndex = 0;
13181     var subIndex = 0;
13182     while(m = re.exec(this.html)){
13183         var name = m[1], content = m[2];
13184         st[subIndex] = {
13185             name: name,
13186             index: subIndex,
13187             buffer: [],
13188             tpl : new Roo.Template(content)
13189         };
13190         if(name){
13191             st[name] = st[subIndex];
13192         }
13193         st[subIndex].tpl.compile();
13194         st[subIndex].tpl.call = this.call.createDelegate(this);
13195         subIndex++;
13196     }
13197     this.subCount = subIndex;
13198     this.subs = st;
13199 };
13200 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13201     /**
13202     * The regular expression used to match sub templates
13203     * @type RegExp
13204     * @property
13205     */
13206     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13207
13208     /**
13209      * Applies the passed values to a child template.
13210      * @param {String/Number} name (optional) The name or index of the child template
13211      * @param {Array/Object} values The values to be applied to the template
13212      * @return {MasterTemplate} this
13213      */
13214      add : function(name, values){
13215         if(arguments.length == 1){
13216             values = arguments[0];
13217             name = 0;
13218         }
13219         var s = this.subs[name];
13220         s.buffer[s.buffer.length] = s.tpl.apply(values);
13221         return this;
13222     },
13223
13224     /**
13225      * Applies all the passed values to a child template.
13226      * @param {String/Number} name (optional) The name or index of the child template
13227      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13228      * @param {Boolean} reset (optional) True to reset the template first
13229      * @return {MasterTemplate} this
13230      */
13231     fill : function(name, values, reset){
13232         var a = arguments;
13233         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13234             values = a[0];
13235             name = 0;
13236             reset = a[1];
13237         }
13238         if(reset){
13239             this.reset();
13240         }
13241         for(var i = 0, len = values.length; i < len; i++){
13242             this.add(name, values[i]);
13243         }
13244         return this;
13245     },
13246
13247     /**
13248      * Resets the template for reuse
13249      * @return {MasterTemplate} this
13250      */
13251      reset : function(){
13252         var s = this.subs;
13253         for(var i = 0; i < this.subCount; i++){
13254             s[i].buffer = [];
13255         }
13256         return this;
13257     },
13258
13259     applyTemplate : function(values){
13260         var s = this.subs;
13261         var replaceIndex = -1;
13262         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13263             return s[++replaceIndex].buffer.join("");
13264         });
13265         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13266     },
13267
13268     apply : function(){
13269         return this.applyTemplate.apply(this, arguments);
13270     },
13271
13272     compile : function(){return this;}
13273 });
13274
13275 /**
13276  * Alias for fill().
13277  * @method
13278  */
13279 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13280  /**
13281  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13282  * var tpl = Roo.MasterTemplate.from('element-id');
13283  * @param {String/HTMLElement} el
13284  * @param {Object} config
13285  * @static
13286  */
13287 Roo.MasterTemplate.from = function(el, config){
13288     el = Roo.getDom(el);
13289     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13290 };/*
13291  * Based on:
13292  * Ext JS Library 1.1.1
13293  * Copyright(c) 2006-2007, Ext JS, LLC.
13294  *
13295  * Originally Released Under LGPL - original licence link has changed is not relivant.
13296  *
13297  * Fork - LGPL
13298  * <script type="text/javascript">
13299  */
13300
13301  
13302 /**
13303  * @class Roo.util.CSS
13304  * Utility class for manipulating CSS rules
13305  * @singleton
13306  */
13307 Roo.util.CSS = function(){
13308         var rules = null;
13309         var doc = document;
13310
13311     var camelRe = /(-[a-z])/gi;
13312     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13313
13314    return {
13315    /**
13316     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13317     * tag and appended to the HEAD of the document.
13318     * @param {String|Object} cssText The text containing the css rules
13319     * @param {String} id An id to add to the stylesheet for later removal
13320     * @return {StyleSheet}
13321     */
13322     createStyleSheet : function(cssText, id){
13323         var ss;
13324         var head = doc.getElementsByTagName("head")[0];
13325         var nrules = doc.createElement("style");
13326         nrules.setAttribute("type", "text/css");
13327         if(id){
13328             nrules.setAttribute("id", id);
13329         }
13330         if (typeof(cssText) != 'string') {
13331             // support object maps..
13332             // not sure if this a good idea.. 
13333             // perhaps it should be merged with the general css handling
13334             // and handle js style props.
13335             var cssTextNew = [];
13336             for(var n in cssText) {
13337                 var citems = [];
13338                 for(var k in cssText[n]) {
13339                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13340                 }
13341                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13342                 
13343             }
13344             cssText = cssTextNew.join("\n");
13345             
13346         }
13347        
13348        
13349        if(Roo.isIE){
13350            head.appendChild(nrules);
13351            ss = nrules.styleSheet;
13352            ss.cssText = cssText;
13353        }else{
13354            try{
13355                 nrules.appendChild(doc.createTextNode(cssText));
13356            }catch(e){
13357                nrules.cssText = cssText; 
13358            }
13359            head.appendChild(nrules);
13360            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13361        }
13362        this.cacheStyleSheet(ss);
13363        return ss;
13364    },
13365
13366    /**
13367     * Removes a style or link tag by id
13368     * @param {String} id The id of the tag
13369     */
13370    removeStyleSheet : function(id){
13371        var existing = doc.getElementById(id);
13372        if(existing){
13373            existing.parentNode.removeChild(existing);
13374        }
13375    },
13376
13377    /**
13378     * Dynamically swaps an existing stylesheet reference for a new one
13379     * @param {String} id The id of an existing link tag to remove
13380     * @param {String} url The href of the new stylesheet to include
13381     */
13382    swapStyleSheet : function(id, url){
13383        this.removeStyleSheet(id);
13384        var ss = doc.createElement("link");
13385        ss.setAttribute("rel", "stylesheet");
13386        ss.setAttribute("type", "text/css");
13387        ss.setAttribute("id", id);
13388        ss.setAttribute("href", url);
13389        doc.getElementsByTagName("head")[0].appendChild(ss);
13390    },
13391    
13392    /**
13393     * Refresh the rule cache if you have dynamically added stylesheets
13394     * @return {Object} An object (hash) of rules indexed by selector
13395     */
13396    refreshCache : function(){
13397        return this.getRules(true);
13398    },
13399
13400    // private
13401    cacheStyleSheet : function(stylesheet){
13402        if(!rules){
13403            rules = {};
13404        }
13405        try{// try catch for cross domain access issue
13406            var ssRules = stylesheet.cssRules || stylesheet.rules;
13407            for(var j = ssRules.length-1; j >= 0; --j){
13408                rules[ssRules[j].selectorText] = ssRules[j];
13409            }
13410        }catch(e){}
13411    },
13412    
13413    /**
13414     * Gets all css rules for the document
13415     * @param {Boolean} refreshCache true to refresh the internal cache
13416     * @return {Object} An object (hash) of rules indexed by selector
13417     */
13418    getRules : function(refreshCache){
13419                 if(rules == null || refreshCache){
13420                         rules = {};
13421                         var ds = doc.styleSheets;
13422                         for(var i =0, len = ds.length; i < len; i++){
13423                             try{
13424                         this.cacheStyleSheet(ds[i]);
13425                     }catch(e){} 
13426                 }
13427                 }
13428                 return rules;
13429         },
13430         
13431         /**
13432     * Gets an an individual CSS rule by selector(s)
13433     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13434     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13435     * @return {CSSRule} The CSS rule or null if one is not found
13436     */
13437    getRule : function(selector, refreshCache){
13438                 var rs = this.getRules(refreshCache);
13439                 if(!(selector instanceof Array)){
13440                     return rs[selector];
13441                 }
13442                 for(var i = 0; i < selector.length; i++){
13443                         if(rs[selector[i]]){
13444                                 return rs[selector[i]];
13445                         }
13446                 }
13447                 return null;
13448         },
13449         
13450         
13451         /**
13452     * Updates a rule property
13453     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13454     * @param {String} property The css property
13455     * @param {String} value The new value for the property
13456     * @return {Boolean} true If a rule was found and updated
13457     */
13458    updateRule : function(selector, property, value){
13459                 if(!(selector instanceof Array)){
13460                         var rule = this.getRule(selector);
13461                         if(rule){
13462                                 rule.style[property.replace(camelRe, camelFn)] = value;
13463                                 return true;
13464                         }
13465                 }else{
13466                         for(var i = 0; i < selector.length; i++){
13467                                 if(this.updateRule(selector[i], property, value)){
13468                                         return true;
13469                                 }
13470                         }
13471                 }
13472                 return false;
13473         }
13474    };   
13475 }();/*
13476  * Based on:
13477  * Ext JS Library 1.1.1
13478  * Copyright(c) 2006-2007, Ext JS, LLC.
13479  *
13480  * Originally Released Under LGPL - original licence link has changed is not relivant.
13481  *
13482  * Fork - LGPL
13483  * <script type="text/javascript">
13484  */
13485
13486  
13487
13488 /**
13489  * @class Roo.util.ClickRepeater
13490  * @extends Roo.util.Observable
13491  * 
13492  * A wrapper class which can be applied to any element. Fires a "click" event while the
13493  * mouse is pressed. The interval between firings may be specified in the config but
13494  * defaults to 10 milliseconds.
13495  * 
13496  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13497  * 
13498  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13499  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13500  * Similar to an autorepeat key delay.
13501  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13502  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13503  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13504  *           "interval" and "delay" are ignored. "immediate" is honored.
13505  * @cfg {Boolean} preventDefault True to prevent the default click event
13506  * @cfg {Boolean} stopDefault True to stop the default click event
13507  * 
13508  * @history
13509  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13510  *     2007-02-02 jvs Renamed to ClickRepeater
13511  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13512  *
13513  *  @constructor
13514  * @param {String/HTMLElement/Element} el The element to listen on
13515  * @param {Object} config
13516  **/
13517 Roo.util.ClickRepeater = function(el, config)
13518 {
13519     this.el = Roo.get(el);
13520     this.el.unselectable();
13521
13522     Roo.apply(this, config);
13523
13524     this.addEvents({
13525     /**
13526      * @event mousedown
13527      * Fires when the mouse button is depressed.
13528      * @param {Roo.util.ClickRepeater} this
13529      */
13530         "mousedown" : true,
13531     /**
13532      * @event click
13533      * Fires on a specified interval during the time the element is pressed.
13534      * @param {Roo.util.ClickRepeater} this
13535      */
13536         "click" : true,
13537     /**
13538      * @event mouseup
13539      * Fires when the mouse key is released.
13540      * @param {Roo.util.ClickRepeater} this
13541      */
13542         "mouseup" : true
13543     });
13544
13545     this.el.on("mousedown", this.handleMouseDown, this);
13546     if(this.preventDefault || this.stopDefault){
13547         this.el.on("click", function(e){
13548             if(this.preventDefault){
13549                 e.preventDefault();
13550             }
13551             if(this.stopDefault){
13552                 e.stopEvent();
13553             }
13554         }, this);
13555     }
13556
13557     // allow inline handler
13558     if(this.handler){
13559         this.on("click", this.handler,  this.scope || this);
13560     }
13561
13562     Roo.util.ClickRepeater.superclass.constructor.call(this);
13563 };
13564
13565 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13566     interval : 20,
13567     delay: 250,
13568     preventDefault : true,
13569     stopDefault : false,
13570     timer : 0,
13571
13572     // private
13573     handleMouseDown : function(){
13574         clearTimeout(this.timer);
13575         this.el.blur();
13576         if(this.pressClass){
13577             this.el.addClass(this.pressClass);
13578         }
13579         this.mousedownTime = new Date();
13580
13581         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13582         this.el.on("mouseout", this.handleMouseOut, this);
13583
13584         this.fireEvent("mousedown", this);
13585         this.fireEvent("click", this);
13586         
13587         this.timer = this.click.defer(this.delay || this.interval, this);
13588     },
13589
13590     // private
13591     click : function(){
13592         this.fireEvent("click", this);
13593         this.timer = this.click.defer(this.getInterval(), this);
13594     },
13595
13596     // private
13597     getInterval: function(){
13598         if(!this.accelerate){
13599             return this.interval;
13600         }
13601         var pressTime = this.mousedownTime.getElapsed();
13602         if(pressTime < 500){
13603             return 400;
13604         }else if(pressTime < 1700){
13605             return 320;
13606         }else if(pressTime < 2600){
13607             return 250;
13608         }else if(pressTime < 3500){
13609             return 180;
13610         }else if(pressTime < 4400){
13611             return 140;
13612         }else if(pressTime < 5300){
13613             return 80;
13614         }else if(pressTime < 6200){
13615             return 50;
13616         }else{
13617             return 10;
13618         }
13619     },
13620
13621     // private
13622     handleMouseOut : function(){
13623         clearTimeout(this.timer);
13624         if(this.pressClass){
13625             this.el.removeClass(this.pressClass);
13626         }
13627         this.el.on("mouseover", this.handleMouseReturn, this);
13628     },
13629
13630     // private
13631     handleMouseReturn : function(){
13632         this.el.un("mouseover", this.handleMouseReturn);
13633         if(this.pressClass){
13634             this.el.addClass(this.pressClass);
13635         }
13636         this.click();
13637     },
13638
13639     // private
13640     handleMouseUp : function(){
13641         clearTimeout(this.timer);
13642         this.el.un("mouseover", this.handleMouseReturn);
13643         this.el.un("mouseout", this.handleMouseOut);
13644         Roo.get(document).un("mouseup", this.handleMouseUp);
13645         this.el.removeClass(this.pressClass);
13646         this.fireEvent("mouseup", this);
13647     }
13648 });/*
13649  * Based on:
13650  * Ext JS Library 1.1.1
13651  * Copyright(c) 2006-2007, Ext JS, LLC.
13652  *
13653  * Originally Released Under LGPL - original licence link has changed is not relivant.
13654  *
13655  * Fork - LGPL
13656  * <script type="text/javascript">
13657  */
13658
13659  
13660 /**
13661  * @class Roo.KeyNav
13662  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13663  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13664  * way to implement custom navigation schemes for any UI component.</p>
13665  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13666  * pageUp, pageDown, del, home, end.  Usage:</p>
13667  <pre><code>
13668 var nav = new Roo.KeyNav("my-element", {
13669     "left" : function(e){
13670         this.moveLeft(e.ctrlKey);
13671     },
13672     "right" : function(e){
13673         this.moveRight(e.ctrlKey);
13674     },
13675     "enter" : function(e){
13676         this.save();
13677     },
13678     scope : this
13679 });
13680 </code></pre>
13681  * @constructor
13682  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13683  * @param {Object} config The config
13684  */
13685 Roo.KeyNav = function(el, config){
13686     this.el = Roo.get(el);
13687     Roo.apply(this, config);
13688     if(!this.disabled){
13689         this.disabled = true;
13690         this.enable();
13691     }
13692 };
13693
13694 Roo.KeyNav.prototype = {
13695     /**
13696      * @cfg {Boolean} disabled
13697      * True to disable this KeyNav instance (defaults to false)
13698      */
13699     disabled : false,
13700     /**
13701      * @cfg {String} defaultEventAction
13702      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13703      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13704      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13705      */
13706     defaultEventAction: "stopEvent",
13707     /**
13708      * @cfg {Boolean} forceKeyDown
13709      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13710      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13711      * handle keydown instead of keypress.
13712      */
13713     forceKeyDown : false,
13714
13715     // private
13716     prepareEvent : function(e){
13717         var k = e.getKey();
13718         var h = this.keyToHandler[k];
13719         //if(h && this[h]){
13720         //    e.stopPropagation();
13721         //}
13722         if(Roo.isSafari && h && k >= 37 && k <= 40){
13723             e.stopEvent();
13724         }
13725     },
13726
13727     // private
13728     relay : function(e){
13729         var k = e.getKey();
13730         var h = this.keyToHandler[k];
13731         if(h && this[h]){
13732             if(this.doRelay(e, this[h], h) !== true){
13733                 e[this.defaultEventAction]();
13734             }
13735         }
13736     },
13737
13738     // private
13739     doRelay : function(e, h, hname){
13740         return h.call(this.scope || this, e);
13741     },
13742
13743     // possible handlers
13744     enter : false,
13745     left : false,
13746     right : false,
13747     up : false,
13748     down : false,
13749     tab : false,
13750     esc : false,
13751     pageUp : false,
13752     pageDown : false,
13753     del : false,
13754     home : false,
13755     end : false,
13756
13757     // quick lookup hash
13758     keyToHandler : {
13759         37 : "left",
13760         39 : "right",
13761         38 : "up",
13762         40 : "down",
13763         33 : "pageUp",
13764         34 : "pageDown",
13765         46 : "del",
13766         36 : "home",
13767         35 : "end",
13768         13 : "enter",
13769         27 : "esc",
13770         9  : "tab"
13771     },
13772
13773         /**
13774          * Enable this KeyNav
13775          */
13776         enable: function(){
13777                 if(this.disabled){
13778             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13779             // the EventObject will normalize Safari automatically
13780             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13781                 this.el.on("keydown", this.relay,  this);
13782             }else{
13783                 this.el.on("keydown", this.prepareEvent,  this);
13784                 this.el.on("keypress", this.relay,  this);
13785             }
13786                     this.disabled = false;
13787                 }
13788         },
13789
13790         /**
13791          * Disable this KeyNav
13792          */
13793         disable: function(){
13794                 if(!this.disabled){
13795                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13796                 this.el.un("keydown", this.relay);
13797             }else{
13798                 this.el.un("keydown", this.prepareEvent);
13799                 this.el.un("keypress", this.relay);
13800             }
13801                     this.disabled = true;
13802                 }
13803         }
13804 };/*
13805  * Based on:
13806  * Ext JS Library 1.1.1
13807  * Copyright(c) 2006-2007, Ext JS, LLC.
13808  *
13809  * Originally Released Under LGPL - original licence link has changed is not relivant.
13810  *
13811  * Fork - LGPL
13812  * <script type="text/javascript">
13813  */
13814
13815  
13816 /**
13817  * @class Roo.KeyMap
13818  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13819  * The constructor accepts the same config object as defined by {@link #addBinding}.
13820  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13821  * combination it will call the function with this signature (if the match is a multi-key
13822  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13823  * A KeyMap can also handle a string representation of keys.<br />
13824  * Usage:
13825  <pre><code>
13826 // map one key by key code
13827 var map = new Roo.KeyMap("my-element", {
13828     key: 13, // or Roo.EventObject.ENTER
13829     fn: myHandler,
13830     scope: myObject
13831 });
13832
13833 // map multiple keys to one action by string
13834 var map = new Roo.KeyMap("my-element", {
13835     key: "a\r\n\t",
13836     fn: myHandler,
13837     scope: myObject
13838 });
13839
13840 // map multiple keys to multiple actions by strings and array of codes
13841 var map = new Roo.KeyMap("my-element", [
13842     {
13843         key: [10,13],
13844         fn: function(){ alert("Return was pressed"); }
13845     }, {
13846         key: "abc",
13847         fn: function(){ alert('a, b or c was pressed'); }
13848     }, {
13849         key: "\t",
13850         ctrl:true,
13851         shift:true,
13852         fn: function(){ alert('Control + shift + tab was pressed.'); }
13853     }
13854 ]);
13855 </code></pre>
13856  * <b>Note: A KeyMap starts enabled</b>
13857  * @constructor
13858  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13859  * @param {Object} config The config (see {@link #addBinding})
13860  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13861  */
13862 Roo.KeyMap = function(el, config, eventName){
13863     this.el  = Roo.get(el);
13864     this.eventName = eventName || "keydown";
13865     this.bindings = [];
13866     if(config){
13867         this.addBinding(config);
13868     }
13869     this.enable();
13870 };
13871
13872 Roo.KeyMap.prototype = {
13873     /**
13874      * True to stop the event from bubbling and prevent the default browser action if the
13875      * key was handled by the KeyMap (defaults to false)
13876      * @type Boolean
13877      */
13878     stopEvent : false,
13879
13880     /**
13881      * Add a new binding to this KeyMap. The following config object properties are supported:
13882      * <pre>
13883 Property    Type             Description
13884 ----------  ---------------  ----------------------------------------------------------------------
13885 key         String/Array     A single keycode or an array of keycodes to handle
13886 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13887 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13888 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13889 fn          Function         The function to call when KeyMap finds the expected key combination
13890 scope       Object           The scope of the callback function
13891 </pre>
13892      *
13893      * Usage:
13894      * <pre><code>
13895 // Create a KeyMap
13896 var map = new Roo.KeyMap(document, {
13897     key: Roo.EventObject.ENTER,
13898     fn: handleKey,
13899     scope: this
13900 });
13901
13902 //Add a new binding to the existing KeyMap later
13903 map.addBinding({
13904     key: 'abc',
13905     shift: true,
13906     fn: handleKey,
13907     scope: this
13908 });
13909 </code></pre>
13910      * @param {Object/Array} config A single KeyMap config or an array of configs
13911      */
13912         addBinding : function(config){
13913         if(config instanceof Array){
13914             for(var i = 0, len = config.length; i < len; i++){
13915                 this.addBinding(config[i]);
13916             }
13917             return;
13918         }
13919         var keyCode = config.key,
13920             shift = config.shift, 
13921             ctrl = config.ctrl, 
13922             alt = config.alt,
13923             fn = config.fn,
13924             scope = config.scope;
13925         if(typeof keyCode == "string"){
13926             var ks = [];
13927             var keyString = keyCode.toUpperCase();
13928             for(var j = 0, len = keyString.length; j < len; j++){
13929                 ks.push(keyString.charCodeAt(j));
13930             }
13931             keyCode = ks;
13932         }
13933         var keyArray = keyCode instanceof Array;
13934         var handler = function(e){
13935             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13936                 var k = e.getKey();
13937                 if(keyArray){
13938                     for(var i = 0, len = keyCode.length; i < len; i++){
13939                         if(keyCode[i] == k){
13940                           if(this.stopEvent){
13941                               e.stopEvent();
13942                           }
13943                           fn.call(scope || window, k, e);
13944                           return;
13945                         }
13946                     }
13947                 }else{
13948                     if(k == keyCode){
13949                         if(this.stopEvent){
13950                            e.stopEvent();
13951                         }
13952                         fn.call(scope || window, k, e);
13953                     }
13954                 }
13955             }
13956         };
13957         this.bindings.push(handler);  
13958         },
13959
13960     /**
13961      * Shorthand for adding a single key listener
13962      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13963      * following options:
13964      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13965      * @param {Function} fn The function to call
13966      * @param {Object} scope (optional) The scope of the function
13967      */
13968     on : function(key, fn, scope){
13969         var keyCode, shift, ctrl, alt;
13970         if(typeof key == "object" && !(key instanceof Array)){
13971             keyCode = key.key;
13972             shift = key.shift;
13973             ctrl = key.ctrl;
13974             alt = key.alt;
13975         }else{
13976             keyCode = key;
13977         }
13978         this.addBinding({
13979             key: keyCode,
13980             shift: shift,
13981             ctrl: ctrl,
13982             alt: alt,
13983             fn: fn,
13984             scope: scope
13985         })
13986     },
13987
13988     // private
13989     handleKeyDown : function(e){
13990             if(this.enabled){ //just in case
13991             var b = this.bindings;
13992             for(var i = 0, len = b.length; i < len; i++){
13993                 b[i].call(this, e);
13994             }
13995             }
13996         },
13997         
13998         /**
13999          * Returns true if this KeyMap is enabled
14000          * @return {Boolean} 
14001          */
14002         isEnabled : function(){
14003             return this.enabled;  
14004         },
14005         
14006         /**
14007          * Enables this KeyMap
14008          */
14009         enable: function(){
14010                 if(!this.enabled){
14011                     this.el.on(this.eventName, this.handleKeyDown, this);
14012                     this.enabled = true;
14013                 }
14014         },
14015
14016         /**
14017          * Disable this KeyMap
14018          */
14019         disable: function(){
14020                 if(this.enabled){
14021                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14022                     this.enabled = false;
14023                 }
14024         }
14025 };/*
14026  * Based on:
14027  * Ext JS Library 1.1.1
14028  * Copyright(c) 2006-2007, Ext JS, LLC.
14029  *
14030  * Originally Released Under LGPL - original licence link has changed is not relivant.
14031  *
14032  * Fork - LGPL
14033  * <script type="text/javascript">
14034  */
14035
14036  
14037 /**
14038  * @class Roo.util.TextMetrics
14039  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14040  * wide, in pixels, a given block of text will be.
14041  * @singleton
14042  */
14043 Roo.util.TextMetrics = function(){
14044     var shared;
14045     return {
14046         /**
14047          * Measures the size of the specified text
14048          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14049          * that can affect the size of the rendered text
14050          * @param {String} text The text to measure
14051          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14052          * in order to accurately measure the text height
14053          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14054          */
14055         measure : function(el, text, fixedWidth){
14056             if(!shared){
14057                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14058             }
14059             shared.bind(el);
14060             shared.setFixedWidth(fixedWidth || 'auto');
14061             return shared.getSize(text);
14062         },
14063
14064         /**
14065          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14066          * the overhead of multiple calls to initialize the style properties on each measurement.
14067          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14068          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14069          * in order to accurately measure the text height
14070          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14071          */
14072         createInstance : function(el, fixedWidth){
14073             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14074         }
14075     };
14076 }();
14077
14078  
14079
14080 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14081     var ml = new Roo.Element(document.createElement('div'));
14082     document.body.appendChild(ml.dom);
14083     ml.position('absolute');
14084     ml.setLeftTop(-1000, -1000);
14085     ml.hide();
14086
14087     if(fixedWidth){
14088         ml.setWidth(fixedWidth);
14089     }
14090      
14091     var instance = {
14092         /**
14093          * Returns the size of the specified text based on the internal element's style and width properties
14094          * @memberOf Roo.util.TextMetrics.Instance#
14095          * @param {String} text The text to measure
14096          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14097          */
14098         getSize : function(text){
14099             ml.update(text);
14100             var s = ml.getSize();
14101             ml.update('');
14102             return s;
14103         },
14104
14105         /**
14106          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14107          * that can affect the size of the rendered text
14108          * @memberOf Roo.util.TextMetrics.Instance#
14109          * @param {String/HTMLElement} el The element, dom node or id
14110          */
14111         bind : function(el){
14112             ml.setStyle(
14113                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14114             );
14115         },
14116
14117         /**
14118          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14119          * to set a fixed width in order to accurately measure the text height.
14120          * @memberOf Roo.util.TextMetrics.Instance#
14121          * @param {Number} width The width to set on the element
14122          */
14123         setFixedWidth : function(width){
14124             ml.setWidth(width);
14125         },
14126
14127         /**
14128          * Returns the measured width of the specified text
14129          * @memberOf Roo.util.TextMetrics.Instance#
14130          * @param {String} text The text to measure
14131          * @return {Number} width The width in pixels
14132          */
14133         getWidth : function(text){
14134             ml.dom.style.width = 'auto';
14135             return this.getSize(text).width;
14136         },
14137
14138         /**
14139          * Returns the measured height of the specified text.  For multiline text, be sure to call
14140          * {@link #setFixedWidth} if necessary.
14141          * @memberOf Roo.util.TextMetrics.Instance#
14142          * @param {String} text The text to measure
14143          * @return {Number} height The height in pixels
14144          */
14145         getHeight : function(text){
14146             return this.getSize(text).height;
14147         }
14148     };
14149
14150     instance.bind(bindTo);
14151
14152     return instance;
14153 };
14154
14155 // backwards compat
14156 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14157  * Based on:
14158  * Ext JS Library 1.1.1
14159  * Copyright(c) 2006-2007, Ext JS, LLC.
14160  *
14161  * Originally Released Under LGPL - original licence link has changed is not relivant.
14162  *
14163  * Fork - LGPL
14164  * <script type="text/javascript">
14165  */
14166
14167 /**
14168  * @class Roo.state.Provider
14169  * Abstract base class for state provider implementations. This class provides methods
14170  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14171  * Provider interface.
14172  */
14173 Roo.state.Provider = function(){
14174     /**
14175      * @event statechange
14176      * Fires when a state change occurs.
14177      * @param {Provider} this This state provider
14178      * @param {String} key The state key which was changed
14179      * @param {String} value The encoded value for the state
14180      */
14181     this.addEvents({
14182         "statechange": true
14183     });
14184     this.state = {};
14185     Roo.state.Provider.superclass.constructor.call(this);
14186 };
14187 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14188     /**
14189      * Returns the current value for a key
14190      * @param {String} name The key name
14191      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14192      * @return {Mixed} The state data
14193      */
14194     get : function(name, defaultValue){
14195         return typeof this.state[name] == "undefined" ?
14196             defaultValue : this.state[name];
14197     },
14198     
14199     /**
14200      * Clears a value from the state
14201      * @param {String} name The key name
14202      */
14203     clear : function(name){
14204         delete this.state[name];
14205         this.fireEvent("statechange", this, name, null);
14206     },
14207     
14208     /**
14209      * Sets the value for a key
14210      * @param {String} name The key name
14211      * @param {Mixed} value The value to set
14212      */
14213     set : function(name, value){
14214         this.state[name] = value;
14215         this.fireEvent("statechange", this, name, value);
14216     },
14217     
14218     /**
14219      * Decodes a string previously encoded with {@link #encodeValue}.
14220      * @param {String} value The value to decode
14221      * @return {Mixed} The decoded value
14222      */
14223     decodeValue : function(cookie){
14224         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14225         var matches = re.exec(unescape(cookie));
14226         if(!matches || !matches[1]) return; // non state cookie
14227         var type = matches[1];
14228         var v = matches[2];
14229         switch(type){
14230             case "n":
14231                 return parseFloat(v);
14232             case "d":
14233                 return new Date(Date.parse(v));
14234             case "b":
14235                 return (v == "1");
14236             case "a":
14237                 var all = [];
14238                 var values = v.split("^");
14239                 for(var i = 0, len = values.length; i < len; i++){
14240                     all.push(this.decodeValue(values[i]));
14241                 }
14242                 return all;
14243            case "o":
14244                 var all = {};
14245                 var values = v.split("^");
14246                 for(var i = 0, len = values.length; i < len; i++){
14247                     var kv = values[i].split("=");
14248                     all[kv[0]] = this.decodeValue(kv[1]);
14249                 }
14250                 return all;
14251            default:
14252                 return v;
14253         }
14254     },
14255     
14256     /**
14257      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14258      * @param {Mixed} value The value to encode
14259      * @return {String} The encoded value
14260      */
14261     encodeValue : function(v){
14262         var enc;
14263         if(typeof v == "number"){
14264             enc = "n:" + v;
14265         }else if(typeof v == "boolean"){
14266             enc = "b:" + (v ? "1" : "0");
14267         }else if(v instanceof Date){
14268             enc = "d:" + v.toGMTString();
14269         }else if(v instanceof Array){
14270             var flat = "";
14271             for(var i = 0, len = v.length; i < len; i++){
14272                 flat += this.encodeValue(v[i]);
14273                 if(i != len-1) flat += "^";
14274             }
14275             enc = "a:" + flat;
14276         }else if(typeof v == "object"){
14277             var flat = "";
14278             for(var key in v){
14279                 if(typeof v[key] != "function"){
14280                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14281                 }
14282             }
14283             enc = "o:" + flat.substring(0, flat.length-1);
14284         }else{
14285             enc = "s:" + v;
14286         }
14287         return escape(enc);        
14288     }
14289 });
14290
14291 /*
14292  * Based on:
14293  * Ext JS Library 1.1.1
14294  * Copyright(c) 2006-2007, Ext JS, LLC.
14295  *
14296  * Originally Released Under LGPL - original licence link has changed is not relivant.
14297  *
14298  * Fork - LGPL
14299  * <script type="text/javascript">
14300  */
14301 /**
14302  * @class Roo.state.Manager
14303  * This is the global state manager. By default all components that are "state aware" check this class
14304  * for state information if you don't pass them a custom state provider. In order for this class
14305  * to be useful, it must be initialized with a provider when your application initializes.
14306  <pre><code>
14307 // in your initialization function
14308 init : function(){
14309    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14310    ...
14311    // supposed you have a {@link Roo.BorderLayout}
14312    var layout = new Roo.BorderLayout(...);
14313    layout.restoreState();
14314    // or a {Roo.BasicDialog}
14315    var dialog = new Roo.BasicDialog(...);
14316    dialog.restoreState();
14317  </code></pre>
14318  * @singleton
14319  */
14320 Roo.state.Manager = function(){
14321     var provider = new Roo.state.Provider();
14322     
14323     return {
14324         /**
14325          * Configures the default state provider for your application
14326          * @param {Provider} stateProvider The state provider to set
14327          */
14328         setProvider : function(stateProvider){
14329             provider = stateProvider;
14330         },
14331         
14332         /**
14333          * Returns the current value for a key
14334          * @param {String} name The key name
14335          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14336          * @return {Mixed} The state data
14337          */
14338         get : function(key, defaultValue){
14339             return provider.get(key, defaultValue);
14340         },
14341         
14342         /**
14343          * Sets the value for a key
14344          * @param {String} name The key name
14345          * @param {Mixed} value The state data
14346          */
14347          set : function(key, value){
14348             provider.set(key, value);
14349         },
14350         
14351         /**
14352          * Clears a value from the state
14353          * @param {String} name The key name
14354          */
14355         clear : function(key){
14356             provider.clear(key);
14357         },
14358         
14359         /**
14360          * Gets the currently configured state provider
14361          * @return {Provider} The state provider
14362          */
14363         getProvider : function(){
14364             return provider;
14365         }
14366     };
14367 }();
14368 /*
14369  * Based on:
14370  * Ext JS Library 1.1.1
14371  * Copyright(c) 2006-2007, Ext JS, LLC.
14372  *
14373  * Originally Released Under LGPL - original licence link has changed is not relivant.
14374  *
14375  * Fork - LGPL
14376  * <script type="text/javascript">
14377  */
14378 /**
14379  * @class Roo.state.CookieProvider
14380  * @extends Roo.state.Provider
14381  * The default Provider implementation which saves state via cookies.
14382  * <br />Usage:
14383  <pre><code>
14384    var cp = new Roo.state.CookieProvider({
14385        path: "/cgi-bin/",
14386        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14387        domain: "roojs.com"
14388    })
14389    Roo.state.Manager.setProvider(cp);
14390  </code></pre>
14391  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14392  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14393  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14394  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14395  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14396  * domain the page is running on including the 'www' like 'www.roojs.com')
14397  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14398  * @constructor
14399  * Create a new CookieProvider
14400  * @param {Object} config The configuration object
14401  */
14402 Roo.state.CookieProvider = function(config){
14403     Roo.state.CookieProvider.superclass.constructor.call(this);
14404     this.path = "/";
14405     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14406     this.domain = null;
14407     this.secure = false;
14408     Roo.apply(this, config);
14409     this.state = this.readCookies();
14410 };
14411
14412 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14413     // private
14414     set : function(name, value){
14415         if(typeof value == "undefined" || value === null){
14416             this.clear(name);
14417             return;
14418         }
14419         this.setCookie(name, value);
14420         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14421     },
14422
14423     // private
14424     clear : function(name){
14425         this.clearCookie(name);
14426         Roo.state.CookieProvider.superclass.clear.call(this, name);
14427     },
14428
14429     // private
14430     readCookies : function(){
14431         var cookies = {};
14432         var c = document.cookie + ";";
14433         var re = /\s?(.*?)=(.*?);/g;
14434         var matches;
14435         while((matches = re.exec(c)) != null){
14436             var name = matches[1];
14437             var value = matches[2];
14438             if(name && name.substring(0,3) == "ys-"){
14439                 cookies[name.substr(3)] = this.decodeValue(value);
14440             }
14441         }
14442         return cookies;
14443     },
14444
14445     // private
14446     setCookie : function(name, value){
14447         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14448            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14449            ((this.path == null) ? "" : ("; path=" + this.path)) +
14450            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14451            ((this.secure == true) ? "; secure" : "");
14452     },
14453
14454     // private
14455     clearCookie : function(name){
14456         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14457            ((this.path == null) ? "" : ("; path=" + this.path)) +
14458            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14459            ((this.secure == true) ? "; secure" : "");
14460     }
14461 });/*
14462  * Based on:
14463  * Ext JS Library 1.1.1
14464  * Copyright(c) 2006-2007, Ext JS, LLC.
14465  *
14466  * Originally Released Under LGPL - original licence link has changed is not relivant.
14467  *
14468  * Fork - LGPL
14469  * <script type="text/javascript">
14470  */
14471
14472
14473
14474 /*
14475  * These classes are derivatives of the similarly named classes in the YUI Library.
14476  * The original license:
14477  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14478  * Code licensed under the BSD License:
14479  * http://developer.yahoo.net/yui/license.txt
14480  */
14481
14482 (function() {
14483
14484 var Event=Roo.EventManager;
14485 var Dom=Roo.lib.Dom;
14486
14487 /**
14488  * @class Roo.dd.DragDrop
14489  * @extends Roo.util.Observable
14490  * Defines the interface and base operation of items that that can be
14491  * dragged or can be drop targets.  It was designed to be extended, overriding
14492  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14493  * Up to three html elements can be associated with a DragDrop instance:
14494  * <ul>
14495  * <li>linked element: the element that is passed into the constructor.
14496  * This is the element which defines the boundaries for interaction with
14497  * other DragDrop objects.</li>
14498  * <li>handle element(s): The drag operation only occurs if the element that
14499  * was clicked matches a handle element.  By default this is the linked
14500  * element, but there are times that you will want only a portion of the
14501  * linked element to initiate the drag operation, and the setHandleElId()
14502  * method provides a way to define this.</li>
14503  * <li>drag element: this represents the element that would be moved along
14504  * with the cursor during a drag operation.  By default, this is the linked
14505  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14506  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14507  * </li>
14508  * </ul>
14509  * This class should not be instantiated until the onload event to ensure that
14510  * the associated elements are available.
14511  * The following would define a DragDrop obj that would interact with any
14512  * other DragDrop obj in the "group1" group:
14513  * <pre>
14514  *  dd = new Roo.dd.DragDrop("div1", "group1");
14515  * </pre>
14516  * Since none of the event handlers have been implemented, nothing would
14517  * actually happen if you were to run the code above.  Normally you would
14518  * override this class or one of the default implementations, but you can
14519  * also override the methods you want on an instance of the class...
14520  * <pre>
14521  *  dd.onDragDrop = function(e, id) {
14522  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14523  *  }
14524  * </pre>
14525  * @constructor
14526  * @param {String} id of the element that is linked to this instance
14527  * @param {String} sGroup the group of related DragDrop objects
14528  * @param {object} config an object containing configurable attributes
14529  *                Valid properties for DragDrop:
14530  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14531  */
14532 Roo.dd.DragDrop = function(id, sGroup, config) {
14533     if (id) {
14534         this.init(id, sGroup, config);
14535     }
14536     
14537 };
14538
14539 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14540
14541     /**
14542      * The id of the element associated with this object.  This is what we
14543      * refer to as the "linked element" because the size and position of
14544      * this element is used to determine when the drag and drop objects have
14545      * interacted.
14546      * @property id
14547      * @type String
14548      */
14549     id: null,
14550
14551     /**
14552      * Configuration attributes passed into the constructor
14553      * @property config
14554      * @type object
14555      */
14556     config: null,
14557
14558     /**
14559      * The id of the element that will be dragged.  By default this is same
14560      * as the linked element , but could be changed to another element. Ex:
14561      * Roo.dd.DDProxy
14562      * @property dragElId
14563      * @type String
14564      * @private
14565      */
14566     dragElId: null,
14567
14568     /**
14569      * the id of the element that initiates the drag operation.  By default
14570      * this is the linked element, but could be changed to be a child of this
14571      * element.  This lets us do things like only starting the drag when the
14572      * header element within the linked html element is clicked.
14573      * @property handleElId
14574      * @type String
14575      * @private
14576      */
14577     handleElId: null,
14578
14579     /**
14580      * An associative array of HTML tags that will be ignored if clicked.
14581      * @property invalidHandleTypes
14582      * @type {string: string}
14583      */
14584     invalidHandleTypes: null,
14585
14586     /**
14587      * An associative array of ids for elements that will be ignored if clicked
14588      * @property invalidHandleIds
14589      * @type {string: string}
14590      */
14591     invalidHandleIds: null,
14592
14593     /**
14594      * An indexted array of css class names for elements that will be ignored
14595      * if clicked.
14596      * @property invalidHandleClasses
14597      * @type string[]
14598      */
14599     invalidHandleClasses: null,
14600
14601     /**
14602      * The linked element's absolute X position at the time the drag was
14603      * started
14604      * @property startPageX
14605      * @type int
14606      * @private
14607      */
14608     startPageX: 0,
14609
14610     /**
14611      * The linked element's absolute X position at the time the drag was
14612      * started
14613      * @property startPageY
14614      * @type int
14615      * @private
14616      */
14617     startPageY: 0,
14618
14619     /**
14620      * The group defines a logical collection of DragDrop objects that are
14621      * related.  Instances only get events when interacting with other
14622      * DragDrop object in the same group.  This lets us define multiple
14623      * groups using a single DragDrop subclass if we want.
14624      * @property groups
14625      * @type {string: string}
14626      */
14627     groups: null,
14628
14629     /**
14630      * Individual drag/drop instances can be locked.  This will prevent
14631      * onmousedown start drag.
14632      * @property locked
14633      * @type boolean
14634      * @private
14635      */
14636     locked: false,
14637
14638     /**
14639      * Lock this instance
14640      * @method lock
14641      */
14642     lock: function() { this.locked = true; },
14643
14644     /**
14645      * Unlock this instace
14646      * @method unlock
14647      */
14648     unlock: function() { this.locked = false; },
14649
14650     /**
14651      * By default, all insances can be a drop target.  This can be disabled by
14652      * setting isTarget to false.
14653      * @method isTarget
14654      * @type boolean
14655      */
14656     isTarget: true,
14657
14658     /**
14659      * The padding configured for this drag and drop object for calculating
14660      * the drop zone intersection with this object.
14661      * @method padding
14662      * @type int[]
14663      */
14664     padding: null,
14665
14666     /**
14667      * Cached reference to the linked element
14668      * @property _domRef
14669      * @private
14670      */
14671     _domRef: null,
14672
14673     /**
14674      * Internal typeof flag
14675      * @property __ygDragDrop
14676      * @private
14677      */
14678     __ygDragDrop: true,
14679
14680     /**
14681      * Set to true when horizontal contraints are applied
14682      * @property constrainX
14683      * @type boolean
14684      * @private
14685      */
14686     constrainX: false,
14687
14688     /**
14689      * Set to true when vertical contraints are applied
14690      * @property constrainY
14691      * @type boolean
14692      * @private
14693      */
14694     constrainY: false,
14695
14696     /**
14697      * The left constraint
14698      * @property minX
14699      * @type int
14700      * @private
14701      */
14702     minX: 0,
14703
14704     /**
14705      * The right constraint
14706      * @property maxX
14707      * @type int
14708      * @private
14709      */
14710     maxX: 0,
14711
14712     /**
14713      * The up constraint
14714      * @property minY
14715      * @type int
14716      * @type int
14717      * @private
14718      */
14719     minY: 0,
14720
14721     /**
14722      * The down constraint
14723      * @property maxY
14724      * @type int
14725      * @private
14726      */
14727     maxY: 0,
14728
14729     /**
14730      * Maintain offsets when we resetconstraints.  Set to true when you want
14731      * the position of the element relative to its parent to stay the same
14732      * when the page changes
14733      *
14734      * @property maintainOffset
14735      * @type boolean
14736      */
14737     maintainOffset: false,
14738
14739     /**
14740      * Array of pixel locations the element will snap to if we specified a
14741      * horizontal graduation/interval.  This array is generated automatically
14742      * when you define a tick interval.
14743      * @property xTicks
14744      * @type int[]
14745      */
14746     xTicks: null,
14747
14748     /**
14749      * Array of pixel locations the element will snap to if we specified a
14750      * vertical graduation/interval.  This array is generated automatically
14751      * when you define a tick interval.
14752      * @property yTicks
14753      * @type int[]
14754      */
14755     yTicks: null,
14756
14757     /**
14758      * By default the drag and drop instance will only respond to the primary
14759      * button click (left button for a right-handed mouse).  Set to true to
14760      * allow drag and drop to start with any mouse click that is propogated
14761      * by the browser
14762      * @property primaryButtonOnly
14763      * @type boolean
14764      */
14765     primaryButtonOnly: true,
14766
14767     /**
14768      * The availabe property is false until the linked dom element is accessible.
14769      * @property available
14770      * @type boolean
14771      */
14772     available: false,
14773
14774     /**
14775      * By default, drags can only be initiated if the mousedown occurs in the
14776      * region the linked element is.  This is done in part to work around a
14777      * bug in some browsers that mis-report the mousedown if the previous
14778      * mouseup happened outside of the window.  This property is set to true
14779      * if outer handles are defined.
14780      *
14781      * @property hasOuterHandles
14782      * @type boolean
14783      * @default false
14784      */
14785     hasOuterHandles: false,
14786
14787     /**
14788      * Code that executes immediately before the startDrag event
14789      * @method b4StartDrag
14790      * @private
14791      */
14792     b4StartDrag: function(x, y) { },
14793
14794     /**
14795      * Abstract method called after a drag/drop object is clicked
14796      * and the drag or mousedown time thresholds have beeen met.
14797      * @method startDrag
14798      * @param {int} X click location
14799      * @param {int} Y click location
14800      */
14801     startDrag: function(x, y) { /* override this */ },
14802
14803     /**
14804      * Code that executes immediately before the onDrag event
14805      * @method b4Drag
14806      * @private
14807      */
14808     b4Drag: function(e) { },
14809
14810     /**
14811      * Abstract method called during the onMouseMove event while dragging an
14812      * object.
14813      * @method onDrag
14814      * @param {Event} e the mousemove event
14815      */
14816     onDrag: function(e) { /* override this */ },
14817
14818     /**
14819      * Abstract method called when this element fist begins hovering over
14820      * another DragDrop obj
14821      * @method onDragEnter
14822      * @param {Event} e the mousemove event
14823      * @param {String|DragDrop[]} id In POINT mode, the element
14824      * id this is hovering over.  In INTERSECT mode, an array of one or more
14825      * dragdrop items being hovered over.
14826      */
14827     onDragEnter: function(e, id) { /* override this */ },
14828
14829     /**
14830      * Code that executes immediately before the onDragOver event
14831      * @method b4DragOver
14832      * @private
14833      */
14834     b4DragOver: function(e) { },
14835
14836     /**
14837      * Abstract method called when this element is hovering over another
14838      * DragDrop obj
14839      * @method onDragOver
14840      * @param {Event} e the mousemove event
14841      * @param {String|DragDrop[]} id In POINT mode, the element
14842      * id this is hovering over.  In INTERSECT mode, an array of dd items
14843      * being hovered over.
14844      */
14845     onDragOver: function(e, id) { /* override this */ },
14846
14847     /**
14848      * Code that executes immediately before the onDragOut event
14849      * @method b4DragOut
14850      * @private
14851      */
14852     b4DragOut: function(e) { },
14853
14854     /**
14855      * Abstract method called when we are no longer hovering over an element
14856      * @method onDragOut
14857      * @param {Event} e the mousemove event
14858      * @param {String|DragDrop[]} id In POINT mode, the element
14859      * id this was hovering over.  In INTERSECT mode, an array of dd items
14860      * that the mouse is no longer over.
14861      */
14862     onDragOut: function(e, id) { /* override this */ },
14863
14864     /**
14865      * Code that executes immediately before the onDragDrop event
14866      * @method b4DragDrop
14867      * @private
14868      */
14869     b4DragDrop: function(e) { },
14870
14871     /**
14872      * Abstract method called when this item is dropped on another DragDrop
14873      * obj
14874      * @method onDragDrop
14875      * @param {Event} e the mouseup event
14876      * @param {String|DragDrop[]} id In POINT mode, the element
14877      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14878      * was dropped on.
14879      */
14880     onDragDrop: function(e, id) { /* override this */ },
14881
14882     /**
14883      * Abstract method called when this item is dropped on an area with no
14884      * drop target
14885      * @method onInvalidDrop
14886      * @param {Event} e the mouseup event
14887      */
14888     onInvalidDrop: function(e) { /* override this */ },
14889
14890     /**
14891      * Code that executes immediately before the endDrag event
14892      * @method b4EndDrag
14893      * @private
14894      */
14895     b4EndDrag: function(e) { },
14896
14897     /**
14898      * Fired when we are done dragging the object
14899      * @method endDrag
14900      * @param {Event} e the mouseup event
14901      */
14902     endDrag: function(e) { /* override this */ },
14903
14904     /**
14905      * Code executed immediately before the onMouseDown event
14906      * @method b4MouseDown
14907      * @param {Event} e the mousedown event
14908      * @private
14909      */
14910     b4MouseDown: function(e) {  },
14911
14912     /**
14913      * Event handler that fires when a drag/drop obj gets a mousedown
14914      * @method onMouseDown
14915      * @param {Event} e the mousedown event
14916      */
14917     onMouseDown: function(e) { /* override this */ },
14918
14919     /**
14920      * Event handler that fires when a drag/drop obj gets a mouseup
14921      * @method onMouseUp
14922      * @param {Event} e the mouseup event
14923      */
14924     onMouseUp: function(e) { /* override this */ },
14925
14926     /**
14927      * Override the onAvailable method to do what is needed after the initial
14928      * position was determined.
14929      * @method onAvailable
14930      */
14931     onAvailable: function () {
14932     },
14933
14934     /*
14935      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14936      * @type Object
14937      */
14938     defaultPadding : {left:0, right:0, top:0, bottom:0},
14939
14940     /*
14941      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14942  *
14943  * Usage:
14944  <pre><code>
14945  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14946                 { dragElId: "existingProxyDiv" });
14947  dd.startDrag = function(){
14948      this.constrainTo("parent-id");
14949  };
14950  </code></pre>
14951  * Or you can initalize it using the {@link Roo.Element} object:
14952  <pre><code>
14953  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14954      startDrag : function(){
14955          this.constrainTo("parent-id");
14956      }
14957  });
14958  </code></pre>
14959      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14960      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14961      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14962      * an object containing the sides to pad. For example: {right:10, bottom:10}
14963      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14964      */
14965     constrainTo : function(constrainTo, pad, inContent){
14966         if(typeof pad == "number"){
14967             pad = {left: pad, right:pad, top:pad, bottom:pad};
14968         }
14969         pad = pad || this.defaultPadding;
14970         var b = Roo.get(this.getEl()).getBox();
14971         var ce = Roo.get(constrainTo);
14972         var s = ce.getScroll();
14973         var c, cd = ce.dom;
14974         if(cd == document.body){
14975             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14976         }else{
14977             xy = ce.getXY();
14978             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14979         }
14980
14981
14982         var topSpace = b.y - c.y;
14983         var leftSpace = b.x - c.x;
14984
14985         this.resetConstraints();
14986         this.setXConstraint(leftSpace - (pad.left||0), // left
14987                 c.width - leftSpace - b.width - (pad.right||0) //right
14988         );
14989         this.setYConstraint(topSpace - (pad.top||0), //top
14990                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14991         );
14992     },
14993
14994     /**
14995      * Returns a reference to the linked element
14996      * @method getEl
14997      * @return {HTMLElement} the html element
14998      */
14999     getEl: function() {
15000         if (!this._domRef) {
15001             this._domRef = Roo.getDom(this.id);
15002         }
15003
15004         return this._domRef;
15005     },
15006
15007     /**
15008      * Returns a reference to the actual element to drag.  By default this is
15009      * the same as the html element, but it can be assigned to another
15010      * element. An example of this can be found in Roo.dd.DDProxy
15011      * @method getDragEl
15012      * @return {HTMLElement} the html element
15013      */
15014     getDragEl: function() {
15015         return Roo.getDom(this.dragElId);
15016     },
15017
15018     /**
15019      * Sets up the DragDrop object.  Must be called in the constructor of any
15020      * Roo.dd.DragDrop subclass
15021      * @method init
15022      * @param id the id of the linked element
15023      * @param {String} sGroup the group of related items
15024      * @param {object} config configuration attributes
15025      */
15026     init: function(id, sGroup, config) {
15027         this.initTarget(id, sGroup, config);
15028         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15029         // Event.on(this.id, "selectstart", Event.preventDefault);
15030     },
15031
15032     /**
15033      * Initializes Targeting functionality only... the object does not
15034      * get a mousedown handler.
15035      * @method initTarget
15036      * @param id the id of the linked element
15037      * @param {String} sGroup the group of related items
15038      * @param {object} config configuration attributes
15039      */
15040     initTarget: function(id, sGroup, config) {
15041
15042         // configuration attributes
15043         this.config = config || {};
15044
15045         // create a local reference to the drag and drop manager
15046         this.DDM = Roo.dd.DDM;
15047         // initialize the groups array
15048         this.groups = {};
15049
15050         // assume that we have an element reference instead of an id if the
15051         // parameter is not a string
15052         if (typeof id !== "string") {
15053             id = Roo.id(id);
15054         }
15055
15056         // set the id
15057         this.id = id;
15058
15059         // add to an interaction group
15060         this.addToGroup((sGroup) ? sGroup : "default");
15061
15062         // We don't want to register this as the handle with the manager
15063         // so we just set the id rather than calling the setter.
15064         this.handleElId = id;
15065
15066         // the linked element is the element that gets dragged by default
15067         this.setDragElId(id);
15068
15069         // by default, clicked anchors will not start drag operations.
15070         this.invalidHandleTypes = { A: "A" };
15071         this.invalidHandleIds = {};
15072         this.invalidHandleClasses = [];
15073
15074         this.applyConfig();
15075
15076         this.handleOnAvailable();
15077     },
15078
15079     /**
15080      * Applies the configuration parameters that were passed into the constructor.
15081      * This is supposed to happen at each level through the inheritance chain.  So
15082      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15083      * DragDrop in order to get all of the parameters that are available in
15084      * each object.
15085      * @method applyConfig
15086      */
15087     applyConfig: function() {
15088
15089         // configurable properties:
15090         //    padding, isTarget, maintainOffset, primaryButtonOnly
15091         this.padding           = this.config.padding || [0, 0, 0, 0];
15092         this.isTarget          = (this.config.isTarget !== false);
15093         this.maintainOffset    = (this.config.maintainOffset);
15094         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15095
15096     },
15097
15098     /**
15099      * Executed when the linked element is available
15100      * @method handleOnAvailable
15101      * @private
15102      */
15103     handleOnAvailable: function() {
15104         this.available = true;
15105         this.resetConstraints();
15106         this.onAvailable();
15107     },
15108
15109      /**
15110      * Configures the padding for the target zone in px.  Effectively expands
15111      * (or reduces) the virtual object size for targeting calculations.
15112      * Supports css-style shorthand; if only one parameter is passed, all sides
15113      * will have that padding, and if only two are passed, the top and bottom
15114      * will have the first param, the left and right the second.
15115      * @method setPadding
15116      * @param {int} iTop    Top pad
15117      * @param {int} iRight  Right pad
15118      * @param {int} iBot    Bot pad
15119      * @param {int} iLeft   Left pad
15120      */
15121     setPadding: function(iTop, iRight, iBot, iLeft) {
15122         // this.padding = [iLeft, iRight, iTop, iBot];
15123         if (!iRight && 0 !== iRight) {
15124             this.padding = [iTop, iTop, iTop, iTop];
15125         } else if (!iBot && 0 !== iBot) {
15126             this.padding = [iTop, iRight, iTop, iRight];
15127         } else {
15128             this.padding = [iTop, iRight, iBot, iLeft];
15129         }
15130     },
15131
15132     /**
15133      * Stores the initial placement of the linked element.
15134      * @method setInitialPosition
15135      * @param {int} diffX   the X offset, default 0
15136      * @param {int} diffY   the Y offset, default 0
15137      */
15138     setInitPosition: function(diffX, diffY) {
15139         var el = this.getEl();
15140
15141         if (!this.DDM.verifyEl(el)) {
15142             return;
15143         }
15144
15145         var dx = diffX || 0;
15146         var dy = diffY || 0;
15147
15148         var p = Dom.getXY( el );
15149
15150         this.initPageX = p[0] - dx;
15151         this.initPageY = p[1] - dy;
15152
15153         this.lastPageX = p[0];
15154         this.lastPageY = p[1];
15155
15156
15157         this.setStartPosition(p);
15158     },
15159
15160     /**
15161      * Sets the start position of the element.  This is set when the obj
15162      * is initialized, the reset when a drag is started.
15163      * @method setStartPosition
15164      * @param pos current position (from previous lookup)
15165      * @private
15166      */
15167     setStartPosition: function(pos) {
15168         var p = pos || Dom.getXY( this.getEl() );
15169         this.deltaSetXY = null;
15170
15171         this.startPageX = p[0];
15172         this.startPageY = p[1];
15173     },
15174
15175     /**
15176      * Add this instance to a group of related drag/drop objects.  All
15177      * instances belong to at least one group, and can belong to as many
15178      * groups as needed.
15179      * @method addToGroup
15180      * @param sGroup {string} the name of the group
15181      */
15182     addToGroup: function(sGroup) {
15183         this.groups[sGroup] = true;
15184         this.DDM.regDragDrop(this, sGroup);
15185     },
15186
15187     /**
15188      * Remove's this instance from the supplied interaction group
15189      * @method removeFromGroup
15190      * @param {string}  sGroup  The group to drop
15191      */
15192     removeFromGroup: function(sGroup) {
15193         if (this.groups[sGroup]) {
15194             delete this.groups[sGroup];
15195         }
15196
15197         this.DDM.removeDDFromGroup(this, sGroup);
15198     },
15199
15200     /**
15201      * Allows you to specify that an element other than the linked element
15202      * will be moved with the cursor during a drag
15203      * @method setDragElId
15204      * @param id {string} the id of the element that will be used to initiate the drag
15205      */
15206     setDragElId: function(id) {
15207         this.dragElId = id;
15208     },
15209
15210     /**
15211      * Allows you to specify a child of the linked element that should be
15212      * used to initiate the drag operation.  An example of this would be if
15213      * you have a content div with text and links.  Clicking anywhere in the
15214      * content area would normally start the drag operation.  Use this method
15215      * to specify that an element inside of the content div is the element
15216      * that starts the drag operation.
15217      * @method setHandleElId
15218      * @param id {string} the id of the element that will be used to
15219      * initiate the drag.
15220      */
15221     setHandleElId: function(id) {
15222         if (typeof id !== "string") {
15223             id = Roo.id(id);
15224         }
15225         this.handleElId = id;
15226         this.DDM.regHandle(this.id, id);
15227     },
15228
15229     /**
15230      * Allows you to set an element outside of the linked element as a drag
15231      * handle
15232      * @method setOuterHandleElId
15233      * @param id the id of the element that will be used to initiate the drag
15234      */
15235     setOuterHandleElId: function(id) {
15236         if (typeof id !== "string") {
15237             id = Roo.id(id);
15238         }
15239         Event.on(id, "mousedown",
15240                 this.handleMouseDown, this);
15241         this.setHandleElId(id);
15242
15243         this.hasOuterHandles = true;
15244     },
15245
15246     /**
15247      * Remove all drag and drop hooks for this element
15248      * @method unreg
15249      */
15250     unreg: function() {
15251         Event.un(this.id, "mousedown",
15252                 this.handleMouseDown);
15253         this._domRef = null;
15254         this.DDM._remove(this);
15255     },
15256
15257     destroy : function(){
15258         this.unreg();
15259     },
15260
15261     /**
15262      * Returns true if this instance is locked, or the drag drop mgr is locked
15263      * (meaning that all drag/drop is disabled on the page.)
15264      * @method isLocked
15265      * @return {boolean} true if this obj or all drag/drop is locked, else
15266      * false
15267      */
15268     isLocked: function() {
15269         return (this.DDM.isLocked() || this.locked);
15270     },
15271
15272     /**
15273      * Fired when this object is clicked
15274      * @method handleMouseDown
15275      * @param {Event} e
15276      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15277      * @private
15278      */
15279     handleMouseDown: function(e, oDD){
15280         if (this.primaryButtonOnly && e.button != 0) {
15281             return;
15282         }
15283
15284         if (this.isLocked()) {
15285             return;
15286         }
15287
15288         this.DDM.refreshCache(this.groups);
15289
15290         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15291         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15292         } else {
15293             if (this.clickValidator(e)) {
15294
15295                 // set the initial element position
15296                 this.setStartPosition();
15297
15298
15299                 this.b4MouseDown(e);
15300                 this.onMouseDown(e);
15301
15302                 this.DDM.handleMouseDown(e, this);
15303
15304                 this.DDM.stopEvent(e);
15305             } else {
15306
15307
15308             }
15309         }
15310     },
15311
15312     clickValidator: function(e) {
15313         var target = e.getTarget();
15314         return ( this.isValidHandleChild(target) &&
15315                     (this.id == this.handleElId ||
15316                         this.DDM.handleWasClicked(target, this.id)) );
15317     },
15318
15319     /**
15320      * Allows you to specify a tag name that should not start a drag operation
15321      * when clicked.  This is designed to facilitate embedding links within a
15322      * drag handle that do something other than start the drag.
15323      * @method addInvalidHandleType
15324      * @param {string} tagName the type of element to exclude
15325      */
15326     addInvalidHandleType: function(tagName) {
15327         var type = tagName.toUpperCase();
15328         this.invalidHandleTypes[type] = type;
15329     },
15330
15331     /**
15332      * Lets you to specify an element id for a child of a drag handle
15333      * that should not initiate a drag
15334      * @method addInvalidHandleId
15335      * @param {string} id the element id of the element you wish to ignore
15336      */
15337     addInvalidHandleId: function(id) {
15338         if (typeof id !== "string") {
15339             id = Roo.id(id);
15340         }
15341         this.invalidHandleIds[id] = id;
15342     },
15343
15344     /**
15345      * Lets you specify a css class of elements that will not initiate a drag
15346      * @method addInvalidHandleClass
15347      * @param {string} cssClass the class of the elements you wish to ignore
15348      */
15349     addInvalidHandleClass: function(cssClass) {
15350         this.invalidHandleClasses.push(cssClass);
15351     },
15352
15353     /**
15354      * Unsets an excluded tag name set by addInvalidHandleType
15355      * @method removeInvalidHandleType
15356      * @param {string} tagName the type of element to unexclude
15357      */
15358     removeInvalidHandleType: function(tagName) {
15359         var type = tagName.toUpperCase();
15360         // this.invalidHandleTypes[type] = null;
15361         delete this.invalidHandleTypes[type];
15362     },
15363
15364     /**
15365      * Unsets an invalid handle id
15366      * @method removeInvalidHandleId
15367      * @param {string} id the id of the element to re-enable
15368      */
15369     removeInvalidHandleId: function(id) {
15370         if (typeof id !== "string") {
15371             id = Roo.id(id);
15372         }
15373         delete this.invalidHandleIds[id];
15374     },
15375
15376     /**
15377      * Unsets an invalid css class
15378      * @method removeInvalidHandleClass
15379      * @param {string} cssClass the class of the element(s) you wish to
15380      * re-enable
15381      */
15382     removeInvalidHandleClass: function(cssClass) {
15383         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15384             if (this.invalidHandleClasses[i] == cssClass) {
15385                 delete this.invalidHandleClasses[i];
15386             }
15387         }
15388     },
15389
15390     /**
15391      * Checks the tag exclusion list to see if this click should be ignored
15392      * @method isValidHandleChild
15393      * @param {HTMLElement} node the HTMLElement to evaluate
15394      * @return {boolean} true if this is a valid tag type, false if not
15395      */
15396     isValidHandleChild: function(node) {
15397
15398         var valid = true;
15399         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15400         var nodeName;
15401         try {
15402             nodeName = node.nodeName.toUpperCase();
15403         } catch(e) {
15404             nodeName = node.nodeName;
15405         }
15406         valid = valid && !this.invalidHandleTypes[nodeName];
15407         valid = valid && !this.invalidHandleIds[node.id];
15408
15409         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15410             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15411         }
15412
15413
15414         return valid;
15415
15416     },
15417
15418     /**
15419      * Create the array of horizontal tick marks if an interval was specified
15420      * in setXConstraint().
15421      * @method setXTicks
15422      * @private
15423      */
15424     setXTicks: function(iStartX, iTickSize) {
15425         this.xTicks = [];
15426         this.xTickSize = iTickSize;
15427
15428         var tickMap = {};
15429
15430         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15431             if (!tickMap[i]) {
15432                 this.xTicks[this.xTicks.length] = i;
15433                 tickMap[i] = true;
15434             }
15435         }
15436
15437         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15438             if (!tickMap[i]) {
15439                 this.xTicks[this.xTicks.length] = i;
15440                 tickMap[i] = true;
15441             }
15442         }
15443
15444         this.xTicks.sort(this.DDM.numericSort) ;
15445     },
15446
15447     /**
15448      * Create the array of vertical tick marks if an interval was specified in
15449      * setYConstraint().
15450      * @method setYTicks
15451      * @private
15452      */
15453     setYTicks: function(iStartY, iTickSize) {
15454         this.yTicks = [];
15455         this.yTickSize = iTickSize;
15456
15457         var tickMap = {};
15458
15459         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15460             if (!tickMap[i]) {
15461                 this.yTicks[this.yTicks.length] = i;
15462                 tickMap[i] = true;
15463             }
15464         }
15465
15466         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15467             if (!tickMap[i]) {
15468                 this.yTicks[this.yTicks.length] = i;
15469                 tickMap[i] = true;
15470             }
15471         }
15472
15473         this.yTicks.sort(this.DDM.numericSort) ;
15474     },
15475
15476     /**
15477      * By default, the element can be dragged any place on the screen.  Use
15478      * this method to limit the horizontal travel of the element.  Pass in
15479      * 0,0 for the parameters if you want to lock the drag to the y axis.
15480      * @method setXConstraint
15481      * @param {int} iLeft the number of pixels the element can move to the left
15482      * @param {int} iRight the number of pixels the element can move to the
15483      * right
15484      * @param {int} iTickSize optional parameter for specifying that the
15485      * element
15486      * should move iTickSize pixels at a time.
15487      */
15488     setXConstraint: function(iLeft, iRight, iTickSize) {
15489         this.leftConstraint = iLeft;
15490         this.rightConstraint = iRight;
15491
15492         this.minX = this.initPageX - iLeft;
15493         this.maxX = this.initPageX + iRight;
15494         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15495
15496         this.constrainX = true;
15497     },
15498
15499     /**
15500      * Clears any constraints applied to this instance.  Also clears ticks
15501      * since they can't exist independent of a constraint at this time.
15502      * @method clearConstraints
15503      */
15504     clearConstraints: function() {
15505         this.constrainX = false;
15506         this.constrainY = false;
15507         this.clearTicks();
15508     },
15509
15510     /**
15511      * Clears any tick interval defined for this instance
15512      * @method clearTicks
15513      */
15514     clearTicks: function() {
15515         this.xTicks = null;
15516         this.yTicks = null;
15517         this.xTickSize = 0;
15518         this.yTickSize = 0;
15519     },
15520
15521     /**
15522      * By default, the element can be dragged any place on the screen.  Set
15523      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15524      * parameters if you want to lock the drag to the x axis.
15525      * @method setYConstraint
15526      * @param {int} iUp the number of pixels the element can move up
15527      * @param {int} iDown the number of pixels the element can move down
15528      * @param {int} iTickSize optional parameter for specifying that the
15529      * element should move iTickSize pixels at a time.
15530      */
15531     setYConstraint: function(iUp, iDown, iTickSize) {
15532         this.topConstraint = iUp;
15533         this.bottomConstraint = iDown;
15534
15535         this.minY = this.initPageY - iUp;
15536         this.maxY = this.initPageY + iDown;
15537         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15538
15539         this.constrainY = true;
15540
15541     },
15542
15543     /**
15544      * resetConstraints must be called if you manually reposition a dd element.
15545      * @method resetConstraints
15546      * @param {boolean} maintainOffset
15547      */
15548     resetConstraints: function() {
15549
15550
15551         // Maintain offsets if necessary
15552         if (this.initPageX || this.initPageX === 0) {
15553             // figure out how much this thing has moved
15554             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15555             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15556
15557             this.setInitPosition(dx, dy);
15558
15559         // This is the first time we have detected the element's position
15560         } else {
15561             this.setInitPosition();
15562         }
15563
15564         if (this.constrainX) {
15565             this.setXConstraint( this.leftConstraint,
15566                                  this.rightConstraint,
15567                                  this.xTickSize        );
15568         }
15569
15570         if (this.constrainY) {
15571             this.setYConstraint( this.topConstraint,
15572                                  this.bottomConstraint,
15573                                  this.yTickSize         );
15574         }
15575     },
15576
15577     /**
15578      * Normally the drag element is moved pixel by pixel, but we can specify
15579      * that it move a number of pixels at a time.  This method resolves the
15580      * location when we have it set up like this.
15581      * @method getTick
15582      * @param {int} val where we want to place the object
15583      * @param {int[]} tickArray sorted array of valid points
15584      * @return {int} the closest tick
15585      * @private
15586      */
15587     getTick: function(val, tickArray) {
15588
15589         if (!tickArray) {
15590             // If tick interval is not defined, it is effectively 1 pixel,
15591             // so we return the value passed to us.
15592             return val;
15593         } else if (tickArray[0] >= val) {
15594             // The value is lower than the first tick, so we return the first
15595             // tick.
15596             return tickArray[0];
15597         } else {
15598             for (var i=0, len=tickArray.length; i<len; ++i) {
15599                 var next = i + 1;
15600                 if (tickArray[next] && tickArray[next] >= val) {
15601                     var diff1 = val - tickArray[i];
15602                     var diff2 = tickArray[next] - val;
15603                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15604                 }
15605             }
15606
15607             // The value is larger than the last tick, so we return the last
15608             // tick.
15609             return tickArray[tickArray.length - 1];
15610         }
15611     },
15612
15613     /**
15614      * toString method
15615      * @method toString
15616      * @return {string} string representation of the dd obj
15617      */
15618     toString: function() {
15619         return ("DragDrop " + this.id);
15620     }
15621
15622 });
15623
15624 })();
15625 /*
15626  * Based on:
15627  * Ext JS Library 1.1.1
15628  * Copyright(c) 2006-2007, Ext JS, LLC.
15629  *
15630  * Originally Released Under LGPL - original licence link has changed is not relivant.
15631  *
15632  * Fork - LGPL
15633  * <script type="text/javascript">
15634  */
15635
15636
15637 /**
15638  * The drag and drop utility provides a framework for building drag and drop
15639  * applications.  In addition to enabling drag and drop for specific elements,
15640  * the drag and drop elements are tracked by the manager class, and the
15641  * interactions between the various elements are tracked during the drag and
15642  * the implementing code is notified about these important moments.
15643  */
15644
15645 // Only load the library once.  Rewriting the manager class would orphan
15646 // existing drag and drop instances.
15647 if (!Roo.dd.DragDropMgr) {
15648
15649 /**
15650  * @class Roo.dd.DragDropMgr
15651  * DragDropMgr is a singleton that tracks the element interaction for
15652  * all DragDrop items in the window.  Generally, you will not call
15653  * this class directly, but it does have helper methods that could
15654  * be useful in your DragDrop implementations.
15655  * @singleton
15656  */
15657 Roo.dd.DragDropMgr = function() {
15658
15659     var Event = Roo.EventManager;
15660
15661     return {
15662
15663         /**
15664          * Two dimensional Array of registered DragDrop objects.  The first
15665          * dimension is the DragDrop item group, the second the DragDrop
15666          * object.
15667          * @property ids
15668          * @type {string: string}
15669          * @private
15670          * @static
15671          */
15672         ids: {},
15673
15674         /**
15675          * Array of element ids defined as drag handles.  Used to determine
15676          * if the element that generated the mousedown event is actually the
15677          * handle and not the html element itself.
15678          * @property handleIds
15679          * @type {string: string}
15680          * @private
15681          * @static
15682          */
15683         handleIds: {},
15684
15685         /**
15686          * the DragDrop object that is currently being dragged
15687          * @property dragCurrent
15688          * @type DragDrop
15689          * @private
15690          * @static
15691          **/
15692         dragCurrent: null,
15693
15694         /**
15695          * the DragDrop object(s) that are being hovered over
15696          * @property dragOvers
15697          * @type Array
15698          * @private
15699          * @static
15700          */
15701         dragOvers: {},
15702
15703         /**
15704          * the X distance between the cursor and the object being dragged
15705          * @property deltaX
15706          * @type int
15707          * @private
15708          * @static
15709          */
15710         deltaX: 0,
15711
15712         /**
15713          * the Y distance between the cursor and the object being dragged
15714          * @property deltaY
15715          * @type int
15716          * @private
15717          * @static
15718          */
15719         deltaY: 0,
15720
15721         /**
15722          * Flag to determine if we should prevent the default behavior of the
15723          * events we define. By default this is true, but this can be set to
15724          * false if you need the default behavior (not recommended)
15725          * @property preventDefault
15726          * @type boolean
15727          * @static
15728          */
15729         preventDefault: true,
15730
15731         /**
15732          * Flag to determine if we should stop the propagation of the events
15733          * we generate. This is true by default but you may want to set it to
15734          * false if the html element contains other features that require the
15735          * mouse click.
15736          * @property stopPropagation
15737          * @type boolean
15738          * @static
15739          */
15740         stopPropagation: true,
15741
15742         /**
15743          * Internal flag that is set to true when drag and drop has been
15744          * intialized
15745          * @property initialized
15746          * @private
15747          * @static
15748          */
15749         initalized: false,
15750
15751         /**
15752          * All drag and drop can be disabled.
15753          * @property locked
15754          * @private
15755          * @static
15756          */
15757         locked: false,
15758
15759         /**
15760          * Called the first time an element is registered.
15761          * @method init
15762          * @private
15763          * @static
15764          */
15765         init: function() {
15766             this.initialized = true;
15767         },
15768
15769         /**
15770          * In point mode, drag and drop interaction is defined by the
15771          * location of the cursor during the drag/drop
15772          * @property POINT
15773          * @type int
15774          * @static
15775          */
15776         POINT: 0,
15777
15778         /**
15779          * In intersect mode, drag and drop interactio nis defined by the
15780          * overlap of two or more drag and drop objects.
15781          * @property INTERSECT
15782          * @type int
15783          * @static
15784          */
15785         INTERSECT: 1,
15786
15787         /**
15788          * The current drag and drop mode.  Default: POINT
15789          * @property mode
15790          * @type int
15791          * @static
15792          */
15793         mode: 0,
15794
15795         /**
15796          * Runs method on all drag and drop objects
15797          * @method _execOnAll
15798          * @private
15799          * @static
15800          */
15801         _execOnAll: function(sMethod, args) {
15802             for (var i in this.ids) {
15803                 for (var j in this.ids[i]) {
15804                     var oDD = this.ids[i][j];
15805                     if (! this.isTypeOfDD(oDD)) {
15806                         continue;
15807                     }
15808                     oDD[sMethod].apply(oDD, args);
15809                 }
15810             }
15811         },
15812
15813         /**
15814          * Drag and drop initialization.  Sets up the global event handlers
15815          * @method _onLoad
15816          * @private
15817          * @static
15818          */
15819         _onLoad: function() {
15820
15821             this.init();
15822
15823
15824             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15825             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15826             Event.on(window,   "unload",    this._onUnload, this, true);
15827             Event.on(window,   "resize",    this._onResize, this, true);
15828             // Event.on(window,   "mouseout",    this._test);
15829
15830         },
15831
15832         /**
15833          * Reset constraints on all drag and drop objs
15834          * @method _onResize
15835          * @private
15836          * @static
15837          */
15838         _onResize: function(e) {
15839             this._execOnAll("resetConstraints", []);
15840         },
15841
15842         /**
15843          * Lock all drag and drop functionality
15844          * @method lock
15845          * @static
15846          */
15847         lock: function() { this.locked = true; },
15848
15849         /**
15850          * Unlock all drag and drop functionality
15851          * @method unlock
15852          * @static
15853          */
15854         unlock: function() { this.locked = false; },
15855
15856         /**
15857          * Is drag and drop locked?
15858          * @method isLocked
15859          * @return {boolean} True if drag and drop is locked, false otherwise.
15860          * @static
15861          */
15862         isLocked: function() { return this.locked; },
15863
15864         /**
15865          * Location cache that is set for all drag drop objects when a drag is
15866          * initiated, cleared when the drag is finished.
15867          * @property locationCache
15868          * @private
15869          * @static
15870          */
15871         locationCache: {},
15872
15873         /**
15874          * Set useCache to false if you want to force object the lookup of each
15875          * drag and drop linked element constantly during a drag.
15876          * @property useCache
15877          * @type boolean
15878          * @static
15879          */
15880         useCache: true,
15881
15882         /**
15883          * The number of pixels that the mouse needs to move after the
15884          * mousedown before the drag is initiated.  Default=3;
15885          * @property clickPixelThresh
15886          * @type int
15887          * @static
15888          */
15889         clickPixelThresh: 3,
15890
15891         /**
15892          * The number of milliseconds after the mousedown event to initiate the
15893          * drag if we don't get a mouseup event. Default=1000
15894          * @property clickTimeThresh
15895          * @type int
15896          * @static
15897          */
15898         clickTimeThresh: 350,
15899
15900         /**
15901          * Flag that indicates that either the drag pixel threshold or the
15902          * mousdown time threshold has been met
15903          * @property dragThreshMet
15904          * @type boolean
15905          * @private
15906          * @static
15907          */
15908         dragThreshMet: false,
15909
15910         /**
15911          * Timeout used for the click time threshold
15912          * @property clickTimeout
15913          * @type Object
15914          * @private
15915          * @static
15916          */
15917         clickTimeout: null,
15918
15919         /**
15920          * The X position of the mousedown event stored for later use when a
15921          * drag threshold is met.
15922          * @property startX
15923          * @type int
15924          * @private
15925          * @static
15926          */
15927         startX: 0,
15928
15929         /**
15930          * The Y position of the mousedown event stored for later use when a
15931          * drag threshold is met.
15932          * @property startY
15933          * @type int
15934          * @private
15935          * @static
15936          */
15937         startY: 0,
15938
15939         /**
15940          * Each DragDrop instance must be registered with the DragDropMgr.
15941          * This is executed in DragDrop.init()
15942          * @method regDragDrop
15943          * @param {DragDrop} oDD the DragDrop object to register
15944          * @param {String} sGroup the name of the group this element belongs to
15945          * @static
15946          */
15947         regDragDrop: function(oDD, sGroup) {
15948             if (!this.initialized) { this.init(); }
15949
15950             if (!this.ids[sGroup]) {
15951                 this.ids[sGroup] = {};
15952             }
15953             this.ids[sGroup][oDD.id] = oDD;
15954         },
15955
15956         /**
15957          * Removes the supplied dd instance from the supplied group. Executed
15958          * by DragDrop.removeFromGroup, so don't call this function directly.
15959          * @method removeDDFromGroup
15960          * @private
15961          * @static
15962          */
15963         removeDDFromGroup: function(oDD, sGroup) {
15964             if (!this.ids[sGroup]) {
15965                 this.ids[sGroup] = {};
15966             }
15967
15968             var obj = this.ids[sGroup];
15969             if (obj && obj[oDD.id]) {
15970                 delete obj[oDD.id];
15971             }
15972         },
15973
15974         /**
15975          * Unregisters a drag and drop item.  This is executed in
15976          * DragDrop.unreg, use that method instead of calling this directly.
15977          * @method _remove
15978          * @private
15979          * @static
15980          */
15981         _remove: function(oDD) {
15982             for (var g in oDD.groups) {
15983                 if (g && this.ids[g][oDD.id]) {
15984                     delete this.ids[g][oDD.id];
15985                 }
15986             }
15987             delete this.handleIds[oDD.id];
15988         },
15989
15990         /**
15991          * Each DragDrop handle element must be registered.  This is done
15992          * automatically when executing DragDrop.setHandleElId()
15993          * @method regHandle
15994          * @param {String} sDDId the DragDrop id this element is a handle for
15995          * @param {String} sHandleId the id of the element that is the drag
15996          * handle
15997          * @static
15998          */
15999         regHandle: function(sDDId, sHandleId) {
16000             if (!this.handleIds[sDDId]) {
16001                 this.handleIds[sDDId] = {};
16002             }
16003             this.handleIds[sDDId][sHandleId] = sHandleId;
16004         },
16005
16006         /**
16007          * Utility function to determine if a given element has been
16008          * registered as a drag drop item.
16009          * @method isDragDrop
16010          * @param {String} id the element id to check
16011          * @return {boolean} true if this element is a DragDrop item,
16012          * false otherwise
16013          * @static
16014          */
16015         isDragDrop: function(id) {
16016             return ( this.getDDById(id) ) ? true : false;
16017         },
16018
16019         /**
16020          * Returns the drag and drop instances that are in all groups the
16021          * passed in instance belongs to.
16022          * @method getRelated
16023          * @param {DragDrop} p_oDD the obj to get related data for
16024          * @param {boolean} bTargetsOnly if true, only return targetable objs
16025          * @return {DragDrop[]} the related instances
16026          * @static
16027          */
16028         getRelated: function(p_oDD, bTargetsOnly) {
16029             var oDDs = [];
16030             for (var i in p_oDD.groups) {
16031                 for (j in this.ids[i]) {
16032                     var dd = this.ids[i][j];
16033                     if (! this.isTypeOfDD(dd)) {
16034                         continue;
16035                     }
16036                     if (!bTargetsOnly || dd.isTarget) {
16037                         oDDs[oDDs.length] = dd;
16038                     }
16039                 }
16040             }
16041
16042             return oDDs;
16043         },
16044
16045         /**
16046          * Returns true if the specified dd target is a legal target for
16047          * the specifice drag obj
16048          * @method isLegalTarget
16049          * @param {DragDrop} the drag obj
16050          * @param {DragDrop} the target
16051          * @return {boolean} true if the target is a legal target for the
16052          * dd obj
16053          * @static
16054          */
16055         isLegalTarget: function (oDD, oTargetDD) {
16056             var targets = this.getRelated(oDD, true);
16057             for (var i=0, len=targets.length;i<len;++i) {
16058                 if (targets[i].id == oTargetDD.id) {
16059                     return true;
16060                 }
16061             }
16062
16063             return false;
16064         },
16065
16066         /**
16067          * My goal is to be able to transparently determine if an object is
16068          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16069          * returns "object", oDD.constructor.toString() always returns
16070          * "DragDrop" and not the name of the subclass.  So for now it just
16071          * evaluates a well-known variable in DragDrop.
16072          * @method isTypeOfDD
16073          * @param {Object} the object to evaluate
16074          * @return {boolean} true if typeof oDD = DragDrop
16075          * @static
16076          */
16077         isTypeOfDD: function (oDD) {
16078             return (oDD && oDD.__ygDragDrop);
16079         },
16080
16081         /**
16082          * Utility function to determine if a given element has been
16083          * registered as a drag drop handle for the given Drag Drop object.
16084          * @method isHandle
16085          * @param {String} id the element id to check
16086          * @return {boolean} true if this element is a DragDrop handle, false
16087          * otherwise
16088          * @static
16089          */
16090         isHandle: function(sDDId, sHandleId) {
16091             return ( this.handleIds[sDDId] &&
16092                             this.handleIds[sDDId][sHandleId] );
16093         },
16094
16095         /**
16096          * Returns the DragDrop instance for a given id
16097          * @method getDDById
16098          * @param {String} id the id of the DragDrop object
16099          * @return {DragDrop} the drag drop object, null if it is not found
16100          * @static
16101          */
16102         getDDById: function(id) {
16103             for (var i in this.ids) {
16104                 if (this.ids[i][id]) {
16105                     return this.ids[i][id];
16106                 }
16107             }
16108             return null;
16109         },
16110
16111         /**
16112          * Fired after a registered DragDrop object gets the mousedown event.
16113          * Sets up the events required to track the object being dragged
16114          * @method handleMouseDown
16115          * @param {Event} e the event
16116          * @param oDD the DragDrop object being dragged
16117          * @private
16118          * @static
16119          */
16120         handleMouseDown: function(e, oDD) {
16121             if(Roo.QuickTips){
16122                 Roo.QuickTips.disable();
16123             }
16124             this.currentTarget = e.getTarget();
16125
16126             this.dragCurrent = oDD;
16127
16128             var el = oDD.getEl();
16129
16130             // track start position
16131             this.startX = e.getPageX();
16132             this.startY = e.getPageY();
16133
16134             this.deltaX = this.startX - el.offsetLeft;
16135             this.deltaY = this.startY - el.offsetTop;
16136
16137             this.dragThreshMet = false;
16138
16139             this.clickTimeout = setTimeout(
16140                     function() {
16141                         var DDM = Roo.dd.DDM;
16142                         DDM.startDrag(DDM.startX, DDM.startY);
16143                     },
16144                     this.clickTimeThresh );
16145         },
16146
16147         /**
16148          * Fired when either the drag pixel threshol or the mousedown hold
16149          * time threshold has been met.
16150          * @method startDrag
16151          * @param x {int} the X position of the original mousedown
16152          * @param y {int} the Y position of the original mousedown
16153          * @static
16154          */
16155         startDrag: function(x, y) {
16156             clearTimeout(this.clickTimeout);
16157             if (this.dragCurrent) {
16158                 this.dragCurrent.b4StartDrag(x, y);
16159                 this.dragCurrent.startDrag(x, y);
16160             }
16161             this.dragThreshMet = true;
16162         },
16163
16164         /**
16165          * Internal function to handle the mouseup event.  Will be invoked
16166          * from the context of the document.
16167          * @method handleMouseUp
16168          * @param {Event} e the event
16169          * @private
16170          * @static
16171          */
16172         handleMouseUp: function(e) {
16173
16174             if(Roo.QuickTips){
16175                 Roo.QuickTips.enable();
16176             }
16177             if (! this.dragCurrent) {
16178                 return;
16179             }
16180
16181             clearTimeout(this.clickTimeout);
16182
16183             if (this.dragThreshMet) {
16184                 this.fireEvents(e, true);
16185             } else {
16186             }
16187
16188             this.stopDrag(e);
16189
16190             this.stopEvent(e);
16191         },
16192
16193         /**
16194          * Utility to stop event propagation and event default, if these
16195          * features are turned on.
16196          * @method stopEvent
16197          * @param {Event} e the event as returned by this.getEvent()
16198          * @static
16199          */
16200         stopEvent: function(e){
16201             if(this.stopPropagation) {
16202                 e.stopPropagation();
16203             }
16204
16205             if (this.preventDefault) {
16206                 e.preventDefault();
16207             }
16208         },
16209
16210         /**
16211          * Internal function to clean up event handlers after the drag
16212          * operation is complete
16213          * @method stopDrag
16214          * @param {Event} e the event
16215          * @private
16216          * @static
16217          */
16218         stopDrag: function(e) {
16219             // Fire the drag end event for the item that was dragged
16220             if (this.dragCurrent) {
16221                 if (this.dragThreshMet) {
16222                     this.dragCurrent.b4EndDrag(e);
16223                     this.dragCurrent.endDrag(e);
16224                 }
16225
16226                 this.dragCurrent.onMouseUp(e);
16227             }
16228
16229             this.dragCurrent = null;
16230             this.dragOvers = {};
16231         },
16232
16233         /**
16234          * Internal function to handle the mousemove event.  Will be invoked
16235          * from the context of the html element.
16236          *
16237          * @TODO figure out what we can do about mouse events lost when the
16238          * user drags objects beyond the window boundary.  Currently we can
16239          * detect this in internet explorer by verifying that the mouse is
16240          * down during the mousemove event.  Firefox doesn't give us the
16241          * button state on the mousemove event.
16242          * @method handleMouseMove
16243          * @param {Event} e the event
16244          * @private
16245          * @static
16246          */
16247         handleMouseMove: function(e) {
16248             if (! this.dragCurrent) {
16249                 return true;
16250             }
16251
16252             // var button = e.which || e.button;
16253
16254             // check for IE mouseup outside of page boundary
16255             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16256                 this.stopEvent(e);
16257                 return this.handleMouseUp(e);
16258             }
16259
16260             if (!this.dragThreshMet) {
16261                 var diffX = Math.abs(this.startX - e.getPageX());
16262                 var diffY = Math.abs(this.startY - e.getPageY());
16263                 if (diffX > this.clickPixelThresh ||
16264                             diffY > this.clickPixelThresh) {
16265                     this.startDrag(this.startX, this.startY);
16266                 }
16267             }
16268
16269             if (this.dragThreshMet) {
16270                 this.dragCurrent.b4Drag(e);
16271                 this.dragCurrent.onDrag(e);
16272                 if(!this.dragCurrent.moveOnly){
16273                     this.fireEvents(e, false);
16274                 }
16275             }
16276
16277             this.stopEvent(e);
16278
16279             return true;
16280         },
16281
16282         /**
16283          * Iterates over all of the DragDrop elements to find ones we are
16284          * hovering over or dropping on
16285          * @method fireEvents
16286          * @param {Event} e the event
16287          * @param {boolean} isDrop is this a drop op or a mouseover op?
16288          * @private
16289          * @static
16290          */
16291         fireEvents: function(e, isDrop) {
16292             var dc = this.dragCurrent;
16293
16294             // If the user did the mouse up outside of the window, we could
16295             // get here even though we have ended the drag.
16296             if (!dc || dc.isLocked()) {
16297                 return;
16298             }
16299
16300             var pt = e.getPoint();
16301
16302             // cache the previous dragOver array
16303             var oldOvers = [];
16304
16305             var outEvts   = [];
16306             var overEvts  = [];
16307             var dropEvts  = [];
16308             var enterEvts = [];
16309
16310             // Check to see if the object(s) we were hovering over is no longer
16311             // being hovered over so we can fire the onDragOut event
16312             for (var i in this.dragOvers) {
16313
16314                 var ddo = this.dragOvers[i];
16315
16316                 if (! this.isTypeOfDD(ddo)) {
16317                     continue;
16318                 }
16319
16320                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16321                     outEvts.push( ddo );
16322                 }
16323
16324                 oldOvers[i] = true;
16325                 delete this.dragOvers[i];
16326             }
16327
16328             for (var sGroup in dc.groups) {
16329
16330                 if ("string" != typeof sGroup) {
16331                     continue;
16332                 }
16333
16334                 for (i in this.ids[sGroup]) {
16335                     var oDD = this.ids[sGroup][i];
16336                     if (! this.isTypeOfDD(oDD)) {
16337                         continue;
16338                     }
16339
16340                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16341                         if (this.isOverTarget(pt, oDD, this.mode)) {
16342                             // look for drop interactions
16343                             if (isDrop) {
16344                                 dropEvts.push( oDD );
16345                             // look for drag enter and drag over interactions
16346                             } else {
16347
16348                                 // initial drag over: dragEnter fires
16349                                 if (!oldOvers[oDD.id]) {
16350                                     enterEvts.push( oDD );
16351                                 // subsequent drag overs: dragOver fires
16352                                 } else {
16353                                     overEvts.push( oDD );
16354                                 }
16355
16356                                 this.dragOvers[oDD.id] = oDD;
16357                             }
16358                         }
16359                     }
16360                 }
16361             }
16362
16363             if (this.mode) {
16364                 if (outEvts.length) {
16365                     dc.b4DragOut(e, outEvts);
16366                     dc.onDragOut(e, outEvts);
16367                 }
16368
16369                 if (enterEvts.length) {
16370                     dc.onDragEnter(e, enterEvts);
16371                 }
16372
16373                 if (overEvts.length) {
16374                     dc.b4DragOver(e, overEvts);
16375                     dc.onDragOver(e, overEvts);
16376                 }
16377
16378                 if (dropEvts.length) {
16379                     dc.b4DragDrop(e, dropEvts);
16380                     dc.onDragDrop(e, dropEvts);
16381                 }
16382
16383             } else {
16384                 // fire dragout events
16385                 var len = 0;
16386                 for (i=0, len=outEvts.length; i<len; ++i) {
16387                     dc.b4DragOut(e, outEvts[i].id);
16388                     dc.onDragOut(e, outEvts[i].id);
16389                 }
16390
16391                 // fire enter events
16392                 for (i=0,len=enterEvts.length; i<len; ++i) {
16393                     // dc.b4DragEnter(e, oDD.id);
16394                     dc.onDragEnter(e, enterEvts[i].id);
16395                 }
16396
16397                 // fire over events
16398                 for (i=0,len=overEvts.length; i<len; ++i) {
16399                     dc.b4DragOver(e, overEvts[i].id);
16400                     dc.onDragOver(e, overEvts[i].id);
16401                 }
16402
16403                 // fire drop events
16404                 for (i=0, len=dropEvts.length; i<len; ++i) {
16405                     dc.b4DragDrop(e, dropEvts[i].id);
16406                     dc.onDragDrop(e, dropEvts[i].id);
16407                 }
16408
16409             }
16410
16411             // notify about a drop that did not find a target
16412             if (isDrop && !dropEvts.length) {
16413                 dc.onInvalidDrop(e);
16414             }
16415
16416         },
16417
16418         /**
16419          * Helper function for getting the best match from the list of drag
16420          * and drop objects returned by the drag and drop events when we are
16421          * in INTERSECT mode.  It returns either the first object that the
16422          * cursor is over, or the object that has the greatest overlap with
16423          * the dragged element.
16424          * @method getBestMatch
16425          * @param  {DragDrop[]} dds The array of drag and drop objects
16426          * targeted
16427          * @return {DragDrop}       The best single match
16428          * @static
16429          */
16430         getBestMatch: function(dds) {
16431             var winner = null;
16432             // Return null if the input is not what we expect
16433             //if (!dds || !dds.length || dds.length == 0) {
16434                // winner = null;
16435             // If there is only one item, it wins
16436             //} else if (dds.length == 1) {
16437
16438             var len = dds.length;
16439
16440             if (len == 1) {
16441                 winner = dds[0];
16442             } else {
16443                 // Loop through the targeted items
16444                 for (var i=0; i<len; ++i) {
16445                     var dd = dds[i];
16446                     // If the cursor is over the object, it wins.  If the
16447                     // cursor is over multiple matches, the first one we come
16448                     // to wins.
16449                     if (dd.cursorIsOver) {
16450                         winner = dd;
16451                         break;
16452                     // Otherwise the object with the most overlap wins
16453                     } else {
16454                         if (!winner ||
16455                             winner.overlap.getArea() < dd.overlap.getArea()) {
16456                             winner = dd;
16457                         }
16458                     }
16459                 }
16460             }
16461
16462             return winner;
16463         },
16464
16465         /**
16466          * Refreshes the cache of the top-left and bottom-right points of the
16467          * drag and drop objects in the specified group(s).  This is in the
16468          * format that is stored in the drag and drop instance, so typical
16469          * usage is:
16470          * <code>
16471          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16472          * </code>
16473          * Alternatively:
16474          * <code>
16475          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16476          * </code>
16477          * @TODO this really should be an indexed array.  Alternatively this
16478          * method could accept both.
16479          * @method refreshCache
16480          * @param {Object} groups an associative array of groups to refresh
16481          * @static
16482          */
16483         refreshCache: function(groups) {
16484             for (var sGroup in groups) {
16485                 if ("string" != typeof sGroup) {
16486                     continue;
16487                 }
16488                 for (var i in this.ids[sGroup]) {
16489                     var oDD = this.ids[sGroup][i];
16490
16491                     if (this.isTypeOfDD(oDD)) {
16492                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16493                         var loc = this.getLocation(oDD);
16494                         if (loc) {
16495                             this.locationCache[oDD.id] = loc;
16496                         } else {
16497                             delete this.locationCache[oDD.id];
16498                             // this will unregister the drag and drop object if
16499                             // the element is not in a usable state
16500                             // oDD.unreg();
16501                         }
16502                     }
16503                 }
16504             }
16505         },
16506
16507         /**
16508          * This checks to make sure an element exists and is in the DOM.  The
16509          * main purpose is to handle cases where innerHTML is used to remove
16510          * drag and drop objects from the DOM.  IE provides an 'unspecified
16511          * error' when trying to access the offsetParent of such an element
16512          * @method verifyEl
16513          * @param {HTMLElement} el the element to check
16514          * @return {boolean} true if the element looks usable
16515          * @static
16516          */
16517         verifyEl: function(el) {
16518             if (el) {
16519                 var parent;
16520                 if(Roo.isIE){
16521                     try{
16522                         parent = el.offsetParent;
16523                     }catch(e){}
16524                 }else{
16525                     parent = el.offsetParent;
16526                 }
16527                 if (parent) {
16528                     return true;
16529                 }
16530             }
16531
16532             return false;
16533         },
16534
16535         /**
16536          * Returns a Region object containing the drag and drop element's position
16537          * and size, including the padding configured for it
16538          * @method getLocation
16539          * @param {DragDrop} oDD the drag and drop object to get the
16540          *                       location for
16541          * @return {Roo.lib.Region} a Region object representing the total area
16542          *                             the element occupies, including any padding
16543          *                             the instance is configured for.
16544          * @static
16545          */
16546         getLocation: function(oDD) {
16547             if (! this.isTypeOfDD(oDD)) {
16548                 return null;
16549             }
16550
16551             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16552
16553             try {
16554                 pos= Roo.lib.Dom.getXY(el);
16555             } catch (e) { }
16556
16557             if (!pos) {
16558                 return null;
16559             }
16560
16561             x1 = pos[0];
16562             x2 = x1 + el.offsetWidth;
16563             y1 = pos[1];
16564             y2 = y1 + el.offsetHeight;
16565
16566             t = y1 - oDD.padding[0];
16567             r = x2 + oDD.padding[1];
16568             b = y2 + oDD.padding[2];
16569             l = x1 - oDD.padding[3];
16570
16571             return new Roo.lib.Region( t, r, b, l );
16572         },
16573
16574         /**
16575          * Checks the cursor location to see if it over the target
16576          * @method isOverTarget
16577          * @param {Roo.lib.Point} pt The point to evaluate
16578          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16579          * @return {boolean} true if the mouse is over the target
16580          * @private
16581          * @static
16582          */
16583         isOverTarget: function(pt, oTarget, intersect) {
16584             // use cache if available
16585             var loc = this.locationCache[oTarget.id];
16586             if (!loc || !this.useCache) {
16587                 loc = this.getLocation(oTarget);
16588                 this.locationCache[oTarget.id] = loc;
16589
16590             }
16591
16592             if (!loc) {
16593                 return false;
16594             }
16595
16596             oTarget.cursorIsOver = loc.contains( pt );
16597
16598             // DragDrop is using this as a sanity check for the initial mousedown
16599             // in this case we are done.  In POINT mode, if the drag obj has no
16600             // contraints, we are also done. Otherwise we need to evaluate the
16601             // location of the target as related to the actual location of the
16602             // dragged element.
16603             var dc = this.dragCurrent;
16604             if (!dc || !dc.getTargetCoord ||
16605                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16606                 return oTarget.cursorIsOver;
16607             }
16608
16609             oTarget.overlap = null;
16610
16611             // Get the current location of the drag element, this is the
16612             // location of the mouse event less the delta that represents
16613             // where the original mousedown happened on the element.  We
16614             // need to consider constraints and ticks as well.
16615             var pos = dc.getTargetCoord(pt.x, pt.y);
16616
16617             var el = dc.getDragEl();
16618             var curRegion = new Roo.lib.Region( pos.y,
16619                                                    pos.x + el.offsetWidth,
16620                                                    pos.y + el.offsetHeight,
16621                                                    pos.x );
16622
16623             var overlap = curRegion.intersect(loc);
16624
16625             if (overlap) {
16626                 oTarget.overlap = overlap;
16627                 return (intersect) ? true : oTarget.cursorIsOver;
16628             } else {
16629                 return false;
16630             }
16631         },
16632
16633         /**
16634          * unload event handler
16635          * @method _onUnload
16636          * @private
16637          * @static
16638          */
16639         _onUnload: function(e, me) {
16640             Roo.dd.DragDropMgr.unregAll();
16641         },
16642
16643         /**
16644          * Cleans up the drag and drop events and objects.
16645          * @method unregAll
16646          * @private
16647          * @static
16648          */
16649         unregAll: function() {
16650
16651             if (this.dragCurrent) {
16652                 this.stopDrag();
16653                 this.dragCurrent = null;
16654             }
16655
16656             this._execOnAll("unreg", []);
16657
16658             for (i in this.elementCache) {
16659                 delete this.elementCache[i];
16660             }
16661
16662             this.elementCache = {};
16663             this.ids = {};
16664         },
16665
16666         /**
16667          * A cache of DOM elements
16668          * @property elementCache
16669          * @private
16670          * @static
16671          */
16672         elementCache: {},
16673
16674         /**
16675          * Get the wrapper for the DOM element specified
16676          * @method getElWrapper
16677          * @param {String} id the id of the element to get
16678          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16679          * @private
16680          * @deprecated This wrapper isn't that useful
16681          * @static
16682          */
16683         getElWrapper: function(id) {
16684             var oWrapper = this.elementCache[id];
16685             if (!oWrapper || !oWrapper.el) {
16686                 oWrapper = this.elementCache[id] =
16687                     new this.ElementWrapper(Roo.getDom(id));
16688             }
16689             return oWrapper;
16690         },
16691
16692         /**
16693          * Returns the actual DOM element
16694          * @method getElement
16695          * @param {String} id the id of the elment to get
16696          * @return {Object} The element
16697          * @deprecated use Roo.getDom instead
16698          * @static
16699          */
16700         getElement: function(id) {
16701             return Roo.getDom(id);
16702         },
16703
16704         /**
16705          * Returns the style property for the DOM element (i.e.,
16706          * document.getElById(id).style)
16707          * @method getCss
16708          * @param {String} id the id of the elment to get
16709          * @return {Object} The style property of the element
16710          * @deprecated use Roo.getDom instead
16711          * @static
16712          */
16713         getCss: function(id) {
16714             var el = Roo.getDom(id);
16715             return (el) ? el.style : null;
16716         },
16717
16718         /**
16719          * Inner class for cached elements
16720          * @class DragDropMgr.ElementWrapper
16721          * @for DragDropMgr
16722          * @private
16723          * @deprecated
16724          */
16725         ElementWrapper: function(el) {
16726                 /**
16727                  * The element
16728                  * @property el
16729                  */
16730                 this.el = el || null;
16731                 /**
16732                  * The element id
16733                  * @property id
16734                  */
16735                 this.id = this.el && el.id;
16736                 /**
16737                  * A reference to the style property
16738                  * @property css
16739                  */
16740                 this.css = this.el && el.style;
16741             },
16742
16743         /**
16744          * Returns the X position of an html element
16745          * @method getPosX
16746          * @param el the element for which to get the position
16747          * @return {int} the X coordinate
16748          * @for DragDropMgr
16749          * @deprecated use Roo.lib.Dom.getX instead
16750          * @static
16751          */
16752         getPosX: function(el) {
16753             return Roo.lib.Dom.getX(el);
16754         },
16755
16756         /**
16757          * Returns the Y position of an html element
16758          * @method getPosY
16759          * @param el the element for which to get the position
16760          * @return {int} the Y coordinate
16761          * @deprecated use Roo.lib.Dom.getY instead
16762          * @static
16763          */
16764         getPosY: function(el) {
16765             return Roo.lib.Dom.getY(el);
16766         },
16767
16768         /**
16769          * Swap two nodes.  In IE, we use the native method, for others we
16770          * emulate the IE behavior
16771          * @method swapNode
16772          * @param n1 the first node to swap
16773          * @param n2 the other node to swap
16774          * @static
16775          */
16776         swapNode: function(n1, n2) {
16777             if (n1.swapNode) {
16778                 n1.swapNode(n2);
16779             } else {
16780                 var p = n2.parentNode;
16781                 var s = n2.nextSibling;
16782
16783                 if (s == n1) {
16784                     p.insertBefore(n1, n2);
16785                 } else if (n2 == n1.nextSibling) {
16786                     p.insertBefore(n2, n1);
16787                 } else {
16788                     n1.parentNode.replaceChild(n2, n1);
16789                     p.insertBefore(n1, s);
16790                 }
16791             }
16792         },
16793
16794         /**
16795          * Returns the current scroll position
16796          * @method getScroll
16797          * @private
16798          * @static
16799          */
16800         getScroll: function () {
16801             var t, l, dde=document.documentElement, db=document.body;
16802             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16803                 t = dde.scrollTop;
16804                 l = dde.scrollLeft;
16805             } else if (db) {
16806                 t = db.scrollTop;
16807                 l = db.scrollLeft;
16808             } else {
16809
16810             }
16811             return { top: t, left: l };
16812         },
16813
16814         /**
16815          * Returns the specified element style property
16816          * @method getStyle
16817          * @param {HTMLElement} el          the element
16818          * @param {string}      styleProp   the style property
16819          * @return {string} The value of the style property
16820          * @deprecated use Roo.lib.Dom.getStyle
16821          * @static
16822          */
16823         getStyle: function(el, styleProp) {
16824             return Roo.fly(el).getStyle(styleProp);
16825         },
16826
16827         /**
16828          * Gets the scrollTop
16829          * @method getScrollTop
16830          * @return {int} the document's scrollTop
16831          * @static
16832          */
16833         getScrollTop: function () { return this.getScroll().top; },
16834
16835         /**
16836          * Gets the scrollLeft
16837          * @method getScrollLeft
16838          * @return {int} the document's scrollTop
16839          * @static
16840          */
16841         getScrollLeft: function () { return this.getScroll().left; },
16842
16843         /**
16844          * Sets the x/y position of an element to the location of the
16845          * target element.
16846          * @method moveToEl
16847          * @param {HTMLElement} moveEl      The element to move
16848          * @param {HTMLElement} targetEl    The position reference element
16849          * @static
16850          */
16851         moveToEl: function (moveEl, targetEl) {
16852             var aCoord = Roo.lib.Dom.getXY(targetEl);
16853             Roo.lib.Dom.setXY(moveEl, aCoord);
16854         },
16855
16856         /**
16857          * Numeric array sort function
16858          * @method numericSort
16859          * @static
16860          */
16861         numericSort: function(a, b) { return (a - b); },
16862
16863         /**
16864          * Internal counter
16865          * @property _timeoutCount
16866          * @private
16867          * @static
16868          */
16869         _timeoutCount: 0,
16870
16871         /**
16872          * Trying to make the load order less important.  Without this we get
16873          * an error if this file is loaded before the Event Utility.
16874          * @method _addListeners
16875          * @private
16876          * @static
16877          */
16878         _addListeners: function() {
16879             var DDM = Roo.dd.DDM;
16880             if ( Roo.lib.Event && document ) {
16881                 DDM._onLoad();
16882             } else {
16883                 if (DDM._timeoutCount > 2000) {
16884                 } else {
16885                     setTimeout(DDM._addListeners, 10);
16886                     if (document && document.body) {
16887                         DDM._timeoutCount += 1;
16888                     }
16889                 }
16890             }
16891         },
16892
16893         /**
16894          * Recursively searches the immediate parent and all child nodes for
16895          * the handle element in order to determine wheter or not it was
16896          * clicked.
16897          * @method handleWasClicked
16898          * @param node the html element to inspect
16899          * @static
16900          */
16901         handleWasClicked: function(node, id) {
16902             if (this.isHandle(id, node.id)) {
16903                 return true;
16904             } else {
16905                 // check to see if this is a text node child of the one we want
16906                 var p = node.parentNode;
16907
16908                 while (p) {
16909                     if (this.isHandle(id, p.id)) {
16910                         return true;
16911                     } else {
16912                         p = p.parentNode;
16913                     }
16914                 }
16915             }
16916
16917             return false;
16918         }
16919
16920     };
16921
16922 }();
16923
16924 // shorter alias, save a few bytes
16925 Roo.dd.DDM = Roo.dd.DragDropMgr;
16926 Roo.dd.DDM._addListeners();
16927
16928 }/*
16929  * Based on:
16930  * Ext JS Library 1.1.1
16931  * Copyright(c) 2006-2007, Ext JS, LLC.
16932  *
16933  * Originally Released Under LGPL - original licence link has changed is not relivant.
16934  *
16935  * Fork - LGPL
16936  * <script type="text/javascript">
16937  */
16938
16939 /**
16940  * @class Roo.dd.DD
16941  * A DragDrop implementation where the linked element follows the
16942  * mouse cursor during a drag.
16943  * @extends Roo.dd.DragDrop
16944  * @constructor
16945  * @param {String} id the id of the linked element
16946  * @param {String} sGroup the group of related DragDrop items
16947  * @param {object} config an object containing configurable attributes
16948  *                Valid properties for DD:
16949  *                    scroll
16950  */
16951 Roo.dd.DD = function(id, sGroup, config) {
16952     if (id) {
16953         this.init(id, sGroup, config);
16954     }
16955 };
16956
16957 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16958
16959     /**
16960      * When set to true, the utility automatically tries to scroll the browser
16961      * window wehn a drag and drop element is dragged near the viewport boundary.
16962      * Defaults to true.
16963      * @property scroll
16964      * @type boolean
16965      */
16966     scroll: true,
16967
16968     /**
16969      * Sets the pointer offset to the distance between the linked element's top
16970      * left corner and the location the element was clicked
16971      * @method autoOffset
16972      * @param {int} iPageX the X coordinate of the click
16973      * @param {int} iPageY the Y coordinate of the click
16974      */
16975     autoOffset: function(iPageX, iPageY) {
16976         var x = iPageX - this.startPageX;
16977         var y = iPageY - this.startPageY;
16978         this.setDelta(x, y);
16979     },
16980
16981     /**
16982      * Sets the pointer offset.  You can call this directly to force the
16983      * offset to be in a particular location (e.g., pass in 0,0 to set it
16984      * to the center of the object)
16985      * @method setDelta
16986      * @param {int} iDeltaX the distance from the left
16987      * @param {int} iDeltaY the distance from the top
16988      */
16989     setDelta: function(iDeltaX, iDeltaY) {
16990         this.deltaX = iDeltaX;
16991         this.deltaY = iDeltaY;
16992     },
16993
16994     /**
16995      * Sets the drag element to the location of the mousedown or click event,
16996      * maintaining the cursor location relative to the location on the element
16997      * that was clicked.  Override this if you want to place the element in a
16998      * location other than where the cursor is.
16999      * @method setDragElPos
17000      * @param {int} iPageX the X coordinate of the mousedown or drag event
17001      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17002      */
17003     setDragElPos: function(iPageX, iPageY) {
17004         // the first time we do this, we are going to check to make sure
17005         // the element has css positioning
17006
17007         var el = this.getDragEl();
17008         this.alignElWithMouse(el, iPageX, iPageY);
17009     },
17010
17011     /**
17012      * Sets the element to the location of the mousedown or click event,
17013      * maintaining the cursor location relative to the location on the element
17014      * that was clicked.  Override this if you want to place the element in a
17015      * location other than where the cursor is.
17016      * @method alignElWithMouse
17017      * @param {HTMLElement} el the element to move
17018      * @param {int} iPageX the X coordinate of the mousedown or drag event
17019      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17020      */
17021     alignElWithMouse: function(el, iPageX, iPageY) {
17022         var oCoord = this.getTargetCoord(iPageX, iPageY);
17023         var fly = el.dom ? el : Roo.fly(el);
17024         if (!this.deltaSetXY) {
17025             var aCoord = [oCoord.x, oCoord.y];
17026             fly.setXY(aCoord);
17027             var newLeft = fly.getLeft(true);
17028             var newTop  = fly.getTop(true);
17029             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17030         } else {
17031             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17032         }
17033
17034         this.cachePosition(oCoord.x, oCoord.y);
17035         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17036         return oCoord;
17037     },
17038
17039     /**
17040      * Saves the most recent position so that we can reset the constraints and
17041      * tick marks on-demand.  We need to know this so that we can calculate the
17042      * number of pixels the element is offset from its original position.
17043      * @method cachePosition
17044      * @param iPageX the current x position (optional, this just makes it so we
17045      * don't have to look it up again)
17046      * @param iPageY the current y position (optional, this just makes it so we
17047      * don't have to look it up again)
17048      */
17049     cachePosition: function(iPageX, iPageY) {
17050         if (iPageX) {
17051             this.lastPageX = iPageX;
17052             this.lastPageY = iPageY;
17053         } else {
17054             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17055             this.lastPageX = aCoord[0];
17056             this.lastPageY = aCoord[1];
17057         }
17058     },
17059
17060     /**
17061      * Auto-scroll the window if the dragged object has been moved beyond the
17062      * visible window boundary.
17063      * @method autoScroll
17064      * @param {int} x the drag element's x position
17065      * @param {int} y the drag element's y position
17066      * @param {int} h the height of the drag element
17067      * @param {int} w the width of the drag element
17068      * @private
17069      */
17070     autoScroll: function(x, y, h, w) {
17071
17072         if (this.scroll) {
17073             // The client height
17074             var clientH = Roo.lib.Dom.getViewWidth();
17075
17076             // The client width
17077             var clientW = Roo.lib.Dom.getViewHeight();
17078
17079             // The amt scrolled down
17080             var st = this.DDM.getScrollTop();
17081
17082             // The amt scrolled right
17083             var sl = this.DDM.getScrollLeft();
17084
17085             // Location of the bottom of the element
17086             var bot = h + y;
17087
17088             // Location of the right of the element
17089             var right = w + x;
17090
17091             // The distance from the cursor to the bottom of the visible area,
17092             // adjusted so that we don't scroll if the cursor is beyond the
17093             // element drag constraints
17094             var toBot = (clientH + st - y - this.deltaY);
17095
17096             // The distance from the cursor to the right of the visible area
17097             var toRight = (clientW + sl - x - this.deltaX);
17098
17099
17100             // How close to the edge the cursor must be before we scroll
17101             // var thresh = (document.all) ? 100 : 40;
17102             var thresh = 40;
17103
17104             // How many pixels to scroll per autoscroll op.  This helps to reduce
17105             // clunky scrolling. IE is more sensitive about this ... it needs this
17106             // value to be higher.
17107             var scrAmt = (document.all) ? 80 : 30;
17108
17109             // Scroll down if we are near the bottom of the visible page and the
17110             // obj extends below the crease
17111             if ( bot > clientH && toBot < thresh ) {
17112                 window.scrollTo(sl, st + scrAmt);
17113             }
17114
17115             // Scroll up if the window is scrolled down and the top of the object
17116             // goes above the top border
17117             if ( y < st && st > 0 && y - st < thresh ) {
17118                 window.scrollTo(sl, st - scrAmt);
17119             }
17120
17121             // Scroll right if the obj is beyond the right border and the cursor is
17122             // near the border.
17123             if ( right > clientW && toRight < thresh ) {
17124                 window.scrollTo(sl + scrAmt, st);
17125             }
17126
17127             // Scroll left if the window has been scrolled to the right and the obj
17128             // extends past the left border
17129             if ( x < sl && sl > 0 && x - sl < thresh ) {
17130                 window.scrollTo(sl - scrAmt, st);
17131             }
17132         }
17133     },
17134
17135     /**
17136      * Finds the location the element should be placed if we want to move
17137      * it to where the mouse location less the click offset would place us.
17138      * @method getTargetCoord
17139      * @param {int} iPageX the X coordinate of the click
17140      * @param {int} iPageY the Y coordinate of the click
17141      * @return an object that contains the coordinates (Object.x and Object.y)
17142      * @private
17143      */
17144     getTargetCoord: function(iPageX, iPageY) {
17145
17146
17147         var x = iPageX - this.deltaX;
17148         var y = iPageY - this.deltaY;
17149
17150         if (this.constrainX) {
17151             if (x < this.minX) { x = this.minX; }
17152             if (x > this.maxX) { x = this.maxX; }
17153         }
17154
17155         if (this.constrainY) {
17156             if (y < this.minY) { y = this.minY; }
17157             if (y > this.maxY) { y = this.maxY; }
17158         }
17159
17160         x = this.getTick(x, this.xTicks);
17161         y = this.getTick(y, this.yTicks);
17162
17163
17164         return {x:x, y:y};
17165     },
17166
17167     /*
17168      * Sets up config options specific to this class. Overrides
17169      * Roo.dd.DragDrop, but all versions of this method through the
17170      * inheritance chain are called
17171      */
17172     applyConfig: function() {
17173         Roo.dd.DD.superclass.applyConfig.call(this);
17174         this.scroll = (this.config.scroll !== false);
17175     },
17176
17177     /*
17178      * Event that fires prior to the onMouseDown event.  Overrides
17179      * Roo.dd.DragDrop.
17180      */
17181     b4MouseDown: function(e) {
17182         // this.resetConstraints();
17183         this.autoOffset(e.getPageX(),
17184                             e.getPageY());
17185     },
17186
17187     /*
17188      * Event that fires prior to the onDrag event.  Overrides
17189      * Roo.dd.DragDrop.
17190      */
17191     b4Drag: function(e) {
17192         this.setDragElPos(e.getPageX(),
17193                             e.getPageY());
17194     },
17195
17196     toString: function() {
17197         return ("DD " + this.id);
17198     }
17199
17200     //////////////////////////////////////////////////////////////////////////
17201     // Debugging ygDragDrop events that can be overridden
17202     //////////////////////////////////////////////////////////////////////////
17203     /*
17204     startDrag: function(x, y) {
17205     },
17206
17207     onDrag: function(e) {
17208     },
17209
17210     onDragEnter: function(e, id) {
17211     },
17212
17213     onDragOver: function(e, id) {
17214     },
17215
17216     onDragOut: function(e, id) {
17217     },
17218
17219     onDragDrop: function(e, id) {
17220     },
17221
17222     endDrag: function(e) {
17223     }
17224
17225     */
17226
17227 });/*
17228  * Based on:
17229  * Ext JS Library 1.1.1
17230  * Copyright(c) 2006-2007, Ext JS, LLC.
17231  *
17232  * Originally Released Under LGPL - original licence link has changed is not relivant.
17233  *
17234  * Fork - LGPL
17235  * <script type="text/javascript">
17236  */
17237
17238 /**
17239  * @class Roo.dd.DDProxy
17240  * A DragDrop implementation that inserts an empty, bordered div into
17241  * the document that follows the cursor during drag operations.  At the time of
17242  * the click, the frame div is resized to the dimensions of the linked html
17243  * element, and moved to the exact location of the linked element.
17244  *
17245  * References to the "frame" element refer to the single proxy element that
17246  * was created to be dragged in place of all DDProxy elements on the
17247  * page.
17248  *
17249  * @extends Roo.dd.DD
17250  * @constructor
17251  * @param {String} id the id of the linked html element
17252  * @param {String} sGroup the group of related DragDrop objects
17253  * @param {object} config an object containing configurable attributes
17254  *                Valid properties for DDProxy in addition to those in DragDrop:
17255  *                   resizeFrame, centerFrame, dragElId
17256  */
17257 Roo.dd.DDProxy = function(id, sGroup, config) {
17258     if (id) {
17259         this.init(id, sGroup, config);
17260         this.initFrame();
17261     }
17262 };
17263
17264 /**
17265  * The default drag frame div id
17266  * @property Roo.dd.DDProxy.dragElId
17267  * @type String
17268  * @static
17269  */
17270 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17271
17272 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17273
17274     /**
17275      * By default we resize the drag frame to be the same size as the element
17276      * we want to drag (this is to get the frame effect).  We can turn it off
17277      * if we want a different behavior.
17278      * @property resizeFrame
17279      * @type boolean
17280      */
17281     resizeFrame: true,
17282
17283     /**
17284      * By default the frame is positioned exactly where the drag element is, so
17285      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17286      * you do not have constraints on the obj is to have the drag frame centered
17287      * around the cursor.  Set centerFrame to true for this effect.
17288      * @property centerFrame
17289      * @type boolean
17290      */
17291     centerFrame: false,
17292
17293     /**
17294      * Creates the proxy element if it does not yet exist
17295      * @method createFrame
17296      */
17297     createFrame: function() {
17298         var self = this;
17299         var body = document.body;
17300
17301         if (!body || !body.firstChild) {
17302             setTimeout( function() { self.createFrame(); }, 50 );
17303             return;
17304         }
17305
17306         var div = this.getDragEl();
17307
17308         if (!div) {
17309             div    = document.createElement("div");
17310             div.id = this.dragElId;
17311             var s  = div.style;
17312
17313             s.position   = "absolute";
17314             s.visibility = "hidden";
17315             s.cursor     = "move";
17316             s.border     = "2px solid #aaa";
17317             s.zIndex     = 999;
17318
17319             // appendChild can blow up IE if invoked prior to the window load event
17320             // while rendering a table.  It is possible there are other scenarios
17321             // that would cause this to happen as well.
17322             body.insertBefore(div, body.firstChild);
17323         }
17324     },
17325
17326     /**
17327      * Initialization for the drag frame element.  Must be called in the
17328      * constructor of all subclasses
17329      * @method initFrame
17330      */
17331     initFrame: function() {
17332         this.createFrame();
17333     },
17334
17335     applyConfig: function() {
17336         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17337
17338         this.resizeFrame = (this.config.resizeFrame !== false);
17339         this.centerFrame = (this.config.centerFrame);
17340         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17341     },
17342
17343     /**
17344      * Resizes the drag frame to the dimensions of the clicked object, positions
17345      * it over the object, and finally displays it
17346      * @method showFrame
17347      * @param {int} iPageX X click position
17348      * @param {int} iPageY Y click position
17349      * @private
17350      */
17351     showFrame: function(iPageX, iPageY) {
17352         var el = this.getEl();
17353         var dragEl = this.getDragEl();
17354         var s = dragEl.style;
17355
17356         this._resizeProxy();
17357
17358         if (this.centerFrame) {
17359             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17360                            Math.round(parseInt(s.height, 10)/2) );
17361         }
17362
17363         this.setDragElPos(iPageX, iPageY);
17364
17365         Roo.fly(dragEl).show();
17366     },
17367
17368     /**
17369      * The proxy is automatically resized to the dimensions of the linked
17370      * element when a drag is initiated, unless resizeFrame is set to false
17371      * @method _resizeProxy
17372      * @private
17373      */
17374     _resizeProxy: function() {
17375         if (this.resizeFrame) {
17376             var el = this.getEl();
17377             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17378         }
17379     },
17380
17381     // overrides Roo.dd.DragDrop
17382     b4MouseDown: function(e) {
17383         var x = e.getPageX();
17384         var y = e.getPageY();
17385         this.autoOffset(x, y);
17386         this.setDragElPos(x, y);
17387     },
17388
17389     // overrides Roo.dd.DragDrop
17390     b4StartDrag: function(x, y) {
17391         // show the drag frame
17392         this.showFrame(x, y);
17393     },
17394
17395     // overrides Roo.dd.DragDrop
17396     b4EndDrag: function(e) {
17397         Roo.fly(this.getDragEl()).hide();
17398     },
17399
17400     // overrides Roo.dd.DragDrop
17401     // By default we try to move the element to the last location of the frame.
17402     // This is so that the default behavior mirrors that of Roo.dd.DD.
17403     endDrag: function(e) {
17404
17405         var lel = this.getEl();
17406         var del = this.getDragEl();
17407
17408         // Show the drag frame briefly so we can get its position
17409         del.style.visibility = "";
17410
17411         this.beforeMove();
17412         // Hide the linked element before the move to get around a Safari
17413         // rendering bug.
17414         lel.style.visibility = "hidden";
17415         Roo.dd.DDM.moveToEl(lel, del);
17416         del.style.visibility = "hidden";
17417         lel.style.visibility = "";
17418
17419         this.afterDrag();
17420     },
17421
17422     beforeMove : function(){
17423
17424     },
17425
17426     afterDrag : function(){
17427
17428     },
17429
17430     toString: function() {
17431         return ("DDProxy " + this.id);
17432     }
17433
17434 });
17435 /*
17436  * Based on:
17437  * Ext JS Library 1.1.1
17438  * Copyright(c) 2006-2007, Ext JS, LLC.
17439  *
17440  * Originally Released Under LGPL - original licence link has changed is not relivant.
17441  *
17442  * Fork - LGPL
17443  * <script type="text/javascript">
17444  */
17445
17446  /**
17447  * @class Roo.dd.DDTarget
17448  * A DragDrop implementation that does not move, but can be a drop
17449  * target.  You would get the same result by simply omitting implementation
17450  * for the event callbacks, but this way we reduce the processing cost of the
17451  * event listener and the callbacks.
17452  * @extends Roo.dd.DragDrop
17453  * @constructor
17454  * @param {String} id the id of the element that is a drop target
17455  * @param {String} sGroup the group of related DragDrop objects
17456  * @param {object} config an object containing configurable attributes
17457  *                 Valid properties for DDTarget in addition to those in
17458  *                 DragDrop:
17459  *                    none
17460  */
17461 Roo.dd.DDTarget = function(id, sGroup, config) {
17462     if (id) {
17463         this.initTarget(id, sGroup, config);
17464     }
17465     if (config.listeners || config.events) { 
17466        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17467             listeners : config.listeners || {}, 
17468             events : config.events || {} 
17469         });    
17470     }
17471 };
17472
17473 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17474 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17475     toString: function() {
17476         return ("DDTarget " + this.id);
17477     }
17478 });
17479 /*
17480  * Based on:
17481  * Ext JS Library 1.1.1
17482  * Copyright(c) 2006-2007, Ext JS, LLC.
17483  *
17484  * Originally Released Under LGPL - original licence link has changed is not relivant.
17485  *
17486  * Fork - LGPL
17487  * <script type="text/javascript">
17488  */
17489  
17490
17491 /**
17492  * @class Roo.dd.ScrollManager
17493  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17494  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17495  * @singleton
17496  */
17497 Roo.dd.ScrollManager = function(){
17498     var ddm = Roo.dd.DragDropMgr;
17499     var els = {};
17500     var dragEl = null;
17501     var proc = {};
17502     
17503     var onStop = function(e){
17504         dragEl = null;
17505         clearProc();
17506     };
17507     
17508     var triggerRefresh = function(){
17509         if(ddm.dragCurrent){
17510              ddm.refreshCache(ddm.dragCurrent.groups);
17511         }
17512     };
17513     
17514     var doScroll = function(){
17515         if(ddm.dragCurrent){
17516             var dds = Roo.dd.ScrollManager;
17517             if(!dds.animate){
17518                 if(proc.el.scroll(proc.dir, dds.increment)){
17519                     triggerRefresh();
17520                 }
17521             }else{
17522                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17523             }
17524         }
17525     };
17526     
17527     var clearProc = function(){
17528         if(proc.id){
17529             clearInterval(proc.id);
17530         }
17531         proc.id = 0;
17532         proc.el = null;
17533         proc.dir = "";
17534     };
17535     
17536     var startProc = function(el, dir){
17537         clearProc();
17538         proc.el = el;
17539         proc.dir = dir;
17540         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17541     };
17542     
17543     var onFire = function(e, isDrop){
17544         if(isDrop || !ddm.dragCurrent){ return; }
17545         var dds = Roo.dd.ScrollManager;
17546         if(!dragEl || dragEl != ddm.dragCurrent){
17547             dragEl = ddm.dragCurrent;
17548             // refresh regions on drag start
17549             dds.refreshCache();
17550         }
17551         
17552         var xy = Roo.lib.Event.getXY(e);
17553         var pt = new Roo.lib.Point(xy[0], xy[1]);
17554         for(var id in els){
17555             var el = els[id], r = el._region;
17556             if(r && r.contains(pt) && el.isScrollable()){
17557                 if(r.bottom - pt.y <= dds.thresh){
17558                     if(proc.el != el){
17559                         startProc(el, "down");
17560                     }
17561                     return;
17562                 }else if(r.right - pt.x <= dds.thresh){
17563                     if(proc.el != el){
17564                         startProc(el, "left");
17565                     }
17566                     return;
17567                 }else if(pt.y - r.top <= dds.thresh){
17568                     if(proc.el != el){
17569                         startProc(el, "up");
17570                     }
17571                     return;
17572                 }else if(pt.x - r.left <= dds.thresh){
17573                     if(proc.el != el){
17574                         startProc(el, "right");
17575                     }
17576                     return;
17577                 }
17578             }
17579         }
17580         clearProc();
17581     };
17582     
17583     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17584     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17585     
17586     return {
17587         /**
17588          * Registers new overflow element(s) to auto scroll
17589          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17590          */
17591         register : function(el){
17592             if(el instanceof Array){
17593                 for(var i = 0, len = el.length; i < len; i++) {
17594                         this.register(el[i]);
17595                 }
17596             }else{
17597                 el = Roo.get(el);
17598                 els[el.id] = el;
17599             }
17600         },
17601         
17602         /**
17603          * Unregisters overflow element(s) so they are no longer scrolled
17604          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17605          */
17606         unregister : function(el){
17607             if(el instanceof Array){
17608                 for(var i = 0, len = el.length; i < len; i++) {
17609                         this.unregister(el[i]);
17610                 }
17611             }else{
17612                 el = Roo.get(el);
17613                 delete els[el.id];
17614             }
17615         },
17616         
17617         /**
17618          * The number of pixels from the edge of a container the pointer needs to be to 
17619          * trigger scrolling (defaults to 25)
17620          * @type Number
17621          */
17622         thresh : 25,
17623         
17624         /**
17625          * The number of pixels to scroll in each scroll increment (defaults to 50)
17626          * @type Number
17627          */
17628         increment : 100,
17629         
17630         /**
17631          * The frequency of scrolls in milliseconds (defaults to 500)
17632          * @type Number
17633          */
17634         frequency : 500,
17635         
17636         /**
17637          * True to animate the scroll (defaults to true)
17638          * @type Boolean
17639          */
17640         animate: true,
17641         
17642         /**
17643          * The animation duration in seconds - 
17644          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17645          * @type Number
17646          */
17647         animDuration: .4,
17648         
17649         /**
17650          * Manually trigger a cache refresh.
17651          */
17652         refreshCache : function(){
17653             for(var id in els){
17654                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17655                     els[id]._region = els[id].getRegion();
17656                 }
17657             }
17658         }
17659     };
17660 }();/*
17661  * Based on:
17662  * Ext JS Library 1.1.1
17663  * Copyright(c) 2006-2007, Ext JS, LLC.
17664  *
17665  * Originally Released Under LGPL - original licence link has changed is not relivant.
17666  *
17667  * Fork - LGPL
17668  * <script type="text/javascript">
17669  */
17670  
17671
17672 /**
17673  * @class Roo.dd.Registry
17674  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17675  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17676  * @singleton
17677  */
17678 Roo.dd.Registry = function(){
17679     var elements = {}; 
17680     var handles = {}; 
17681     var autoIdSeed = 0;
17682
17683     var getId = function(el, autogen){
17684         if(typeof el == "string"){
17685             return el;
17686         }
17687         var id = el.id;
17688         if(!id && autogen !== false){
17689             id = "roodd-" + (++autoIdSeed);
17690             el.id = id;
17691         }
17692         return id;
17693     };
17694     
17695     return {
17696     /**
17697      * Register a drag drop element
17698      * @param {String|HTMLElement} element The id or DOM node to register
17699      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17700      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17701      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17702      * populated in the data object (if applicable):
17703      * <pre>
17704 Value      Description<br />
17705 ---------  ------------------------------------------<br />
17706 handles    Array of DOM nodes that trigger dragging<br />
17707            for the element being registered<br />
17708 isHandle   True if the element passed in triggers<br />
17709            dragging itself, else false
17710 </pre>
17711      */
17712         register : function(el, data){
17713             data = data || {};
17714             if(typeof el == "string"){
17715                 el = document.getElementById(el);
17716             }
17717             data.ddel = el;
17718             elements[getId(el)] = data;
17719             if(data.isHandle !== false){
17720                 handles[data.ddel.id] = data;
17721             }
17722             if(data.handles){
17723                 var hs = data.handles;
17724                 for(var i = 0, len = hs.length; i < len; i++){
17725                         handles[getId(hs[i])] = data;
17726                 }
17727             }
17728         },
17729
17730     /**
17731      * Unregister a drag drop element
17732      * @param {String|HTMLElement}  element The id or DOM node to unregister
17733      */
17734         unregister : function(el){
17735             var id = getId(el, false);
17736             var data = elements[id];
17737             if(data){
17738                 delete elements[id];
17739                 if(data.handles){
17740                     var hs = data.handles;
17741                     for(var i = 0, len = hs.length; i < len; i++){
17742                         delete handles[getId(hs[i], false)];
17743                     }
17744                 }
17745             }
17746         },
17747
17748     /**
17749      * Returns the handle registered for a DOM Node by id
17750      * @param {String|HTMLElement} id The DOM node or id to look up
17751      * @return {Object} handle The custom handle data
17752      */
17753         getHandle : function(id){
17754             if(typeof id != "string"){ // must be element?
17755                 id = id.id;
17756             }
17757             return handles[id];
17758         },
17759
17760     /**
17761      * Returns the handle that is registered for the DOM node that is the target of the event
17762      * @param {Event} e The event
17763      * @return {Object} handle The custom handle data
17764      */
17765         getHandleFromEvent : function(e){
17766             var t = Roo.lib.Event.getTarget(e);
17767             return t ? handles[t.id] : null;
17768         },
17769
17770     /**
17771      * Returns a custom data object that is registered for a DOM node by id
17772      * @param {String|HTMLElement} id The DOM node or id to look up
17773      * @return {Object} data The custom data
17774      */
17775         getTarget : function(id){
17776             if(typeof id != "string"){ // must be element?
17777                 id = id.id;
17778             }
17779             return elements[id];
17780         },
17781
17782     /**
17783      * Returns a custom data object that is registered for the DOM node that is the target of the event
17784      * @param {Event} e The event
17785      * @return {Object} data The custom data
17786      */
17787         getTargetFromEvent : function(e){
17788             var t = Roo.lib.Event.getTarget(e);
17789             return t ? elements[t.id] || handles[t.id] : null;
17790         }
17791     };
17792 }();/*
17793  * Based on:
17794  * Ext JS Library 1.1.1
17795  * Copyright(c) 2006-2007, Ext JS, LLC.
17796  *
17797  * Originally Released Under LGPL - original licence link has changed is not relivant.
17798  *
17799  * Fork - LGPL
17800  * <script type="text/javascript">
17801  */
17802  
17803
17804 /**
17805  * @class Roo.dd.StatusProxy
17806  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17807  * default drag proxy used by all Roo.dd components.
17808  * @constructor
17809  * @param {Object} config
17810  */
17811 Roo.dd.StatusProxy = function(config){
17812     Roo.apply(this, config);
17813     this.id = this.id || Roo.id();
17814     this.el = new Roo.Layer({
17815         dh: {
17816             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17817                 {tag: "div", cls: "x-dd-drop-icon"},
17818                 {tag: "div", cls: "x-dd-drag-ghost"}
17819             ]
17820         }, 
17821         shadow: !config || config.shadow !== false
17822     });
17823     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17824     this.dropStatus = this.dropNotAllowed;
17825 };
17826
17827 Roo.dd.StatusProxy.prototype = {
17828     /**
17829      * @cfg {String} dropAllowed
17830      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17831      */
17832     dropAllowed : "x-dd-drop-ok",
17833     /**
17834      * @cfg {String} dropNotAllowed
17835      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17836      */
17837     dropNotAllowed : "x-dd-drop-nodrop",
17838
17839     /**
17840      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17841      * over the current target element.
17842      * @param {String} cssClass The css class for the new drop status indicator image
17843      */
17844     setStatus : function(cssClass){
17845         cssClass = cssClass || this.dropNotAllowed;
17846         if(this.dropStatus != cssClass){
17847             this.el.replaceClass(this.dropStatus, cssClass);
17848             this.dropStatus = cssClass;
17849         }
17850     },
17851
17852     /**
17853      * Resets the status indicator to the default dropNotAllowed value
17854      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17855      */
17856     reset : function(clearGhost){
17857         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17858         this.dropStatus = this.dropNotAllowed;
17859         if(clearGhost){
17860             this.ghost.update("");
17861         }
17862     },
17863
17864     /**
17865      * Updates the contents of the ghost element
17866      * @param {String} html The html that will replace the current innerHTML of the ghost element
17867      */
17868     update : function(html){
17869         if(typeof html == "string"){
17870             this.ghost.update(html);
17871         }else{
17872             this.ghost.update("");
17873             html.style.margin = "0";
17874             this.ghost.dom.appendChild(html);
17875         }
17876         // ensure float = none set?? cant remember why though.
17877         var el = this.ghost.dom.firstChild;
17878                 if(el){
17879                         Roo.fly(el).setStyle('float', 'none');
17880                 }
17881     },
17882     
17883     /**
17884      * Returns the underlying proxy {@link Roo.Layer}
17885      * @return {Roo.Layer} el
17886     */
17887     getEl : function(){
17888         return this.el;
17889     },
17890
17891     /**
17892      * Returns the ghost element
17893      * @return {Roo.Element} el
17894      */
17895     getGhost : function(){
17896         return this.ghost;
17897     },
17898
17899     /**
17900      * Hides the proxy
17901      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17902      */
17903     hide : function(clear){
17904         this.el.hide();
17905         if(clear){
17906             this.reset(true);
17907         }
17908     },
17909
17910     /**
17911      * Stops the repair animation if it's currently running
17912      */
17913     stop : function(){
17914         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17915             this.anim.stop();
17916         }
17917     },
17918
17919     /**
17920      * Displays this proxy
17921      */
17922     show : function(){
17923         this.el.show();
17924     },
17925
17926     /**
17927      * Force the Layer to sync its shadow and shim positions to the element
17928      */
17929     sync : function(){
17930         this.el.sync();
17931     },
17932
17933     /**
17934      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17935      * invalid drop operation by the item being dragged.
17936      * @param {Array} xy The XY position of the element ([x, y])
17937      * @param {Function} callback The function to call after the repair is complete
17938      * @param {Object} scope The scope in which to execute the callback
17939      */
17940     repair : function(xy, callback, scope){
17941         this.callback = callback;
17942         this.scope = scope;
17943         if(xy && this.animRepair !== false){
17944             this.el.addClass("x-dd-drag-repair");
17945             this.el.hideUnders(true);
17946             this.anim = this.el.shift({
17947                 duration: this.repairDuration || .5,
17948                 easing: 'easeOut',
17949                 xy: xy,
17950                 stopFx: true,
17951                 callback: this.afterRepair,
17952                 scope: this
17953             });
17954         }else{
17955             this.afterRepair();
17956         }
17957     },
17958
17959     // private
17960     afterRepair : function(){
17961         this.hide(true);
17962         if(typeof this.callback == "function"){
17963             this.callback.call(this.scope || this);
17964         }
17965         this.callback = null;
17966         this.scope = null;
17967     }
17968 };/*
17969  * Based on:
17970  * Ext JS Library 1.1.1
17971  * Copyright(c) 2006-2007, Ext JS, LLC.
17972  *
17973  * Originally Released Under LGPL - original licence link has changed is not relivant.
17974  *
17975  * Fork - LGPL
17976  * <script type="text/javascript">
17977  */
17978
17979 /**
17980  * @class Roo.dd.DragSource
17981  * @extends Roo.dd.DDProxy
17982  * A simple class that provides the basic implementation needed to make any element draggable.
17983  * @constructor
17984  * @param {String/HTMLElement/Element} el The container element
17985  * @param {Object} config
17986  */
17987 Roo.dd.DragSource = function(el, config){
17988     this.el = Roo.get(el);
17989     this.dragData = {};
17990     
17991     Roo.apply(this, config);
17992     
17993     if(!this.proxy){
17994         this.proxy = new Roo.dd.StatusProxy();
17995     }
17996
17997     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17998           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17999     
18000     this.dragging = false;
18001 };
18002
18003 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18004     /**
18005      * @cfg {String} dropAllowed
18006      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18007      */
18008     dropAllowed : "x-dd-drop-ok",
18009     /**
18010      * @cfg {String} dropNotAllowed
18011      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18012      */
18013     dropNotAllowed : "x-dd-drop-nodrop",
18014
18015     /**
18016      * Returns the data object associated with this drag source
18017      * @return {Object} data An object containing arbitrary data
18018      */
18019     getDragData : function(e){
18020         return this.dragData;
18021     },
18022
18023     // private
18024     onDragEnter : function(e, id){
18025         var target = Roo.dd.DragDropMgr.getDDById(id);
18026         this.cachedTarget = target;
18027         if(this.beforeDragEnter(target, e, id) !== false){
18028             if(target.isNotifyTarget){
18029                 var status = target.notifyEnter(this, e, this.dragData);
18030                 this.proxy.setStatus(status);
18031             }else{
18032                 this.proxy.setStatus(this.dropAllowed);
18033             }
18034             
18035             if(this.afterDragEnter){
18036                 /**
18037                  * An empty function by default, but provided so that you can perform a custom action
18038                  * when the dragged item enters the drop target by providing an implementation.
18039                  * @param {Roo.dd.DragDrop} target The drop target
18040                  * @param {Event} e The event object
18041                  * @param {String} id The id of the dragged element
18042                  * @method afterDragEnter
18043                  */
18044                 this.afterDragEnter(target, e, id);
18045             }
18046         }
18047     },
18048
18049     /**
18050      * An empty function by default, but provided so that you can perform a custom action
18051      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18052      * @param {Roo.dd.DragDrop} target The drop target
18053      * @param {Event} e The event object
18054      * @param {String} id The id of the dragged element
18055      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18056      */
18057     beforeDragEnter : function(target, e, id){
18058         return true;
18059     },
18060
18061     // private
18062     alignElWithMouse: function() {
18063         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18064         this.proxy.sync();
18065     },
18066
18067     // private
18068     onDragOver : function(e, id){
18069         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18070         if(this.beforeDragOver(target, e, id) !== false){
18071             if(target.isNotifyTarget){
18072                 var status = target.notifyOver(this, e, this.dragData);
18073                 this.proxy.setStatus(status);
18074             }
18075
18076             if(this.afterDragOver){
18077                 /**
18078                  * An empty function by default, but provided so that you can perform a custom action
18079                  * while the dragged item is over the drop target by providing an implementation.
18080                  * @param {Roo.dd.DragDrop} target The drop target
18081                  * @param {Event} e The event object
18082                  * @param {String} id The id of the dragged element
18083                  * @method afterDragOver
18084                  */
18085                 this.afterDragOver(target, e, id);
18086             }
18087         }
18088     },
18089
18090     /**
18091      * An empty function by default, but provided so that you can perform a custom action
18092      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18093      * @param {Roo.dd.DragDrop} target The drop target
18094      * @param {Event} e The event object
18095      * @param {String} id The id of the dragged element
18096      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18097      */
18098     beforeDragOver : function(target, e, id){
18099         return true;
18100     },
18101
18102     // private
18103     onDragOut : function(e, id){
18104         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18105         if(this.beforeDragOut(target, e, id) !== false){
18106             if(target.isNotifyTarget){
18107                 target.notifyOut(this, e, this.dragData);
18108             }
18109             this.proxy.reset();
18110             if(this.afterDragOut){
18111                 /**
18112                  * An empty function by default, but provided so that you can perform a custom action
18113                  * after the dragged item is dragged out of the target without dropping.
18114                  * @param {Roo.dd.DragDrop} target The drop target
18115                  * @param {Event} e The event object
18116                  * @param {String} id The id of the dragged element
18117                  * @method afterDragOut
18118                  */
18119                 this.afterDragOut(target, e, id);
18120             }
18121         }
18122         this.cachedTarget = null;
18123     },
18124
18125     /**
18126      * An empty function by default, but provided so that you can perform a custom action before the dragged
18127      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18128      * @param {Roo.dd.DragDrop} target The drop target
18129      * @param {Event} e The event object
18130      * @param {String} id The id of the dragged element
18131      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18132      */
18133     beforeDragOut : function(target, e, id){
18134         return true;
18135     },
18136     
18137     // private
18138     onDragDrop : function(e, id){
18139         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18140         if(this.beforeDragDrop(target, e, id) !== false){
18141             if(target.isNotifyTarget){
18142                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18143                     this.onValidDrop(target, e, id);
18144                 }else{
18145                     this.onInvalidDrop(target, e, id);
18146                 }
18147             }else{
18148                 this.onValidDrop(target, e, id);
18149             }
18150             
18151             if(this.afterDragDrop){
18152                 /**
18153                  * An empty function by default, but provided so that you can perform a custom action
18154                  * after a valid drag drop has occurred by providing an implementation.
18155                  * @param {Roo.dd.DragDrop} target The drop target
18156                  * @param {Event} e The event object
18157                  * @param {String} id The id of the dropped element
18158                  * @method afterDragDrop
18159                  */
18160                 this.afterDragDrop(target, e, id);
18161             }
18162         }
18163         delete this.cachedTarget;
18164     },
18165
18166     /**
18167      * An empty function by default, but provided so that you can perform a custom action before the dragged
18168      * item is dropped onto the target and optionally cancel the onDragDrop.
18169      * @param {Roo.dd.DragDrop} target The drop target
18170      * @param {Event} e The event object
18171      * @param {String} id The id of the dragged element
18172      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18173      */
18174     beforeDragDrop : function(target, e, id){
18175         return true;
18176     },
18177
18178     // private
18179     onValidDrop : function(target, e, id){
18180         this.hideProxy();
18181         if(this.afterValidDrop){
18182             /**
18183              * An empty function by default, but provided so that you can perform a custom action
18184              * after a valid drop has occurred by providing an implementation.
18185              * @param {Object} target The target DD 
18186              * @param {Event} e The event object
18187              * @param {String} id The id of the dropped element
18188              * @method afterInvalidDrop
18189              */
18190             this.afterValidDrop(target, e, id);
18191         }
18192     },
18193
18194     // private
18195     getRepairXY : function(e, data){
18196         return this.el.getXY();  
18197     },
18198
18199     // private
18200     onInvalidDrop : function(target, e, id){
18201         this.beforeInvalidDrop(target, e, id);
18202         if(this.cachedTarget){
18203             if(this.cachedTarget.isNotifyTarget){
18204                 this.cachedTarget.notifyOut(this, e, this.dragData);
18205             }
18206             this.cacheTarget = null;
18207         }
18208         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18209
18210         if(this.afterInvalidDrop){
18211             /**
18212              * An empty function by default, but provided so that you can perform a custom action
18213              * after an invalid drop has occurred by providing an implementation.
18214              * @param {Event} e The event object
18215              * @param {String} id The id of the dropped element
18216              * @method afterInvalidDrop
18217              */
18218             this.afterInvalidDrop(e, id);
18219         }
18220     },
18221
18222     // private
18223     afterRepair : function(){
18224         if(Roo.enableFx){
18225             this.el.highlight(this.hlColor || "c3daf9");
18226         }
18227         this.dragging = false;
18228     },
18229
18230     /**
18231      * An empty function by default, but provided so that you can perform a custom action after an invalid
18232      * drop has occurred.
18233      * @param {Roo.dd.DragDrop} target The drop target
18234      * @param {Event} e The event object
18235      * @param {String} id The id of the dragged element
18236      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18237      */
18238     beforeInvalidDrop : function(target, e, id){
18239         return true;
18240     },
18241
18242     // private
18243     handleMouseDown : function(e){
18244         if(this.dragging) {
18245             return;
18246         }
18247         var data = this.getDragData(e);
18248         if(data && this.onBeforeDrag(data, e) !== false){
18249             this.dragData = data;
18250             this.proxy.stop();
18251             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18252         } 
18253     },
18254
18255     /**
18256      * An empty function by default, but provided so that you can perform a custom action before the initial
18257      * drag event begins and optionally cancel it.
18258      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18259      * @param {Event} e The event object
18260      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18261      */
18262     onBeforeDrag : function(data, e){
18263         return true;
18264     },
18265
18266     /**
18267      * An empty function by default, but provided so that you can perform a custom action once the initial
18268      * drag event has begun.  The drag cannot be canceled from this function.
18269      * @param {Number} x The x position of the click on the dragged object
18270      * @param {Number} y The y position of the click on the dragged object
18271      */
18272     onStartDrag : Roo.emptyFn,
18273
18274     // private - YUI override
18275     startDrag : function(x, y){
18276         this.proxy.reset();
18277         this.dragging = true;
18278         this.proxy.update("");
18279         this.onInitDrag(x, y);
18280         this.proxy.show();
18281     },
18282
18283     // private
18284     onInitDrag : function(x, y){
18285         var clone = this.el.dom.cloneNode(true);
18286         clone.id = Roo.id(); // prevent duplicate ids
18287         this.proxy.update(clone);
18288         this.onStartDrag(x, y);
18289         return true;
18290     },
18291
18292     /**
18293      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18294      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18295      */
18296     getProxy : function(){
18297         return this.proxy;  
18298     },
18299
18300     /**
18301      * Hides the drag source's {@link Roo.dd.StatusProxy}
18302      */
18303     hideProxy : function(){
18304         this.proxy.hide();  
18305         this.proxy.reset(true);
18306         this.dragging = false;
18307     },
18308
18309     // private
18310     triggerCacheRefresh : function(){
18311         Roo.dd.DDM.refreshCache(this.groups);
18312     },
18313
18314     // private - override to prevent hiding
18315     b4EndDrag: function(e) {
18316     },
18317
18318     // private - override to prevent moving
18319     endDrag : function(e){
18320         this.onEndDrag(this.dragData, e);
18321     },
18322
18323     // private
18324     onEndDrag : function(data, e){
18325     },
18326     
18327     // private - pin to cursor
18328     autoOffset : function(x, y) {
18329         this.setDelta(-12, -20);
18330     }    
18331 });/*
18332  * Based on:
18333  * Ext JS Library 1.1.1
18334  * Copyright(c) 2006-2007, Ext JS, LLC.
18335  *
18336  * Originally Released Under LGPL - original licence link has changed is not relivant.
18337  *
18338  * Fork - LGPL
18339  * <script type="text/javascript">
18340  */
18341
18342
18343 /**
18344  * @class Roo.dd.DropTarget
18345  * @extends Roo.dd.DDTarget
18346  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18347  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18348  * @constructor
18349  * @param {String/HTMLElement/Element} el The container element
18350  * @param {Object} config
18351  */
18352 Roo.dd.DropTarget = function(el, config){
18353     this.el = Roo.get(el);
18354     
18355     var listeners = false; ;
18356     if (config && config.listeners) {
18357         listeners= config.listeners;
18358         delete config.listeners;
18359     }
18360     Roo.apply(this, config);
18361     
18362     if(this.containerScroll){
18363         Roo.dd.ScrollManager.register(this.el);
18364     }
18365     this.addEvents( {
18366          /**
18367          * @scope Roo.dd.DropTarget
18368          */
18369          
18370          /**
18371          * @event enter
18372          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18373          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18374          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18375          * 
18376          * IMPORTANT : it should set this.overClass and this.dropAllowed
18377          * 
18378          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18379          * @param {Event} e The event
18380          * @param {Object} data An object containing arbitrary data supplied by the drag source
18381          */
18382         "enter" : true,
18383         
18384          /**
18385          * @event over
18386          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18387          * This method will be called on every mouse movement while the drag source is over the drop target.
18388          * This default implementation simply returns the dropAllowed config value.
18389          * 
18390          * IMPORTANT : it should set this.dropAllowed
18391          * 
18392          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18393          * @param {Event} e The event
18394          * @param {Object} data An object containing arbitrary data supplied by the drag source
18395          
18396          */
18397         "over" : true,
18398         /**
18399          * @event out
18400          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18401          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18402          * overClass (if any) from the drop element.
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          "out" : true,
18408          
18409         /**
18410          * @event drop
18411          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18412          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18413          * implementation that does something to process the drop event and returns true so that the drag source's
18414          * repair action does not run.
18415          * 
18416          * IMPORTANT : it should set this.success
18417          * 
18418          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18419          * @param {Event} e The event
18420          * @param {Object} data An object containing arbitrary data supplied by the drag source
18421         */
18422          "drop" : true
18423     });
18424             
18425      
18426     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18427         this.el.dom, 
18428         this.ddGroup || this.group,
18429         {
18430             isTarget: true,
18431             listeners : listeners || {} 
18432            
18433         
18434         }
18435     );
18436
18437 };
18438
18439 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18440     /**
18441      * @cfg {String} overClass
18442      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18443      */
18444      /**
18445      * @cfg {String} ddGroup
18446      * The drag drop group to handle drop events for
18447      */
18448      
18449     /**
18450      * @cfg {String} dropAllowed
18451      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18452      */
18453     dropAllowed : "x-dd-drop-ok",
18454     /**
18455      * @cfg {String} dropNotAllowed
18456      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18457      */
18458     dropNotAllowed : "x-dd-drop-nodrop",
18459     /**
18460      * @cfg {boolean} success
18461      * set this after drop listener.. 
18462      */
18463     success : false,
18464     /**
18465      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18466      * if the drop point is valid for over/enter..
18467      */
18468     valid : false,
18469     // private
18470     isTarget : true,
18471
18472     // private
18473     isNotifyTarget : true,
18474     
18475     /**
18476      * @hide
18477      */
18478     notifyEnter : function(dd, e, data)
18479     {
18480         this.valid = true;
18481         this.fireEvent('enter', dd, e, data);
18482         if(this.overClass){
18483             this.el.addClass(this.overClass);
18484         }
18485         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18486             this.valid ? this.dropAllowed : this.dropNotAllowed
18487         );
18488     },
18489
18490     /**
18491      * @hide
18492      */
18493     notifyOver : function(dd, e, data)
18494     {
18495         this.valid = true;
18496         this.fireEvent('over', dd, e, data);
18497         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18498             this.valid ? this.dropAllowed : this.dropNotAllowed
18499         );
18500     },
18501
18502     /**
18503      * @hide
18504      */
18505     notifyOut : function(dd, e, data)
18506     {
18507         this.fireEvent('out', dd, e, data);
18508         if(this.overClass){
18509             this.el.removeClass(this.overClass);
18510         }
18511     },
18512
18513     /**
18514      * @hide
18515      */
18516     notifyDrop : function(dd, e, data)
18517     {
18518         this.success = false;
18519         this.fireEvent('drop', dd, e, data);
18520         return this.success;
18521     }
18522 });/*
18523  * Based on:
18524  * Ext JS Library 1.1.1
18525  * Copyright(c) 2006-2007, Ext JS, LLC.
18526  *
18527  * Originally Released Under LGPL - original licence link has changed is not relivant.
18528  *
18529  * Fork - LGPL
18530  * <script type="text/javascript">
18531  */
18532
18533
18534 /**
18535  * @class Roo.dd.DragZone
18536  * @extends Roo.dd.DragSource
18537  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18538  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18539  * @constructor
18540  * @param {String/HTMLElement/Element} el The container element
18541  * @param {Object} config
18542  */
18543 Roo.dd.DragZone = function(el, config){
18544     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18545     if(this.containerScroll){
18546         Roo.dd.ScrollManager.register(this.el);
18547     }
18548 };
18549
18550 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18551     /**
18552      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18553      * for auto scrolling during drag operations.
18554      */
18555     /**
18556      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18557      * method after a failed drop (defaults to "c3daf9" - light blue)
18558      */
18559
18560     /**
18561      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18562      * for a valid target to drag based on the mouse down. Override this method
18563      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18564      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18565      * @param {EventObject} e The mouse down event
18566      * @return {Object} The dragData
18567      */
18568     getDragData : function(e){
18569         return Roo.dd.Registry.getHandleFromEvent(e);
18570     },
18571     
18572     /**
18573      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18574      * this.dragData.ddel
18575      * @param {Number} x The x position of the click on the dragged object
18576      * @param {Number} y The y position of the click on the dragged object
18577      * @return {Boolean} true to continue the drag, false to cancel
18578      */
18579     onInitDrag : function(x, y){
18580         this.proxy.update(this.dragData.ddel.cloneNode(true));
18581         this.onStartDrag(x, y);
18582         return true;
18583     },
18584     
18585     /**
18586      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18587      */
18588     afterRepair : function(){
18589         if(Roo.enableFx){
18590             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18591         }
18592         this.dragging = false;
18593     },
18594
18595     /**
18596      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18597      * the XY of this.dragData.ddel
18598      * @param {EventObject} e The mouse up event
18599      * @return {Array} The xy location (e.g. [100, 200])
18600      */
18601     getRepairXY : function(e){
18602         return Roo.Element.fly(this.dragData.ddel).getXY();  
18603     }
18604 });/*
18605  * Based on:
18606  * Ext JS Library 1.1.1
18607  * Copyright(c) 2006-2007, Ext JS, LLC.
18608  *
18609  * Originally Released Under LGPL - original licence link has changed is not relivant.
18610  *
18611  * Fork - LGPL
18612  * <script type="text/javascript">
18613  */
18614 /**
18615  * @class Roo.dd.DropZone
18616  * @extends Roo.dd.DropTarget
18617  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18618  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18619  * @constructor
18620  * @param {String/HTMLElement/Element} el The container element
18621  * @param {Object} config
18622  */
18623 Roo.dd.DropZone = function(el, config){
18624     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18625 };
18626
18627 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18628     /**
18629      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18630      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18631      * provide your own custom lookup.
18632      * @param {Event} e The event
18633      * @return {Object} data The custom data
18634      */
18635     getTargetFromEvent : function(e){
18636         return Roo.dd.Registry.getTargetFromEvent(e);
18637     },
18638
18639     /**
18640      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18641      * that it has registered.  This method has no default implementation and should be overridden to provide
18642      * node-specific processing if necessary.
18643      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18644      * {@link #getTargetFromEvent} for this node)
18645      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18646      * @param {Event} e The event
18647      * @param {Object} data An object containing arbitrary data supplied by the drag source
18648      */
18649     onNodeEnter : function(n, dd, e, data){
18650         
18651     },
18652
18653     /**
18654      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18655      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18656      * overridden to provide the proper feedback.
18657      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18658      * {@link #getTargetFromEvent} for this node)
18659      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18660      * @param {Event} e The event
18661      * @param {Object} data An object containing arbitrary data supplied by the drag source
18662      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18663      * underlying {@link Roo.dd.StatusProxy} can be updated
18664      */
18665     onNodeOver : function(n, dd, e, data){
18666         return this.dropAllowed;
18667     },
18668
18669     /**
18670      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18671      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18672      * node-specific processing if necessary.
18673      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18674      * {@link #getTargetFromEvent} for this node)
18675      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18676      * @param {Event} e The event
18677      * @param {Object} data An object containing arbitrary data supplied by the drag source
18678      */
18679     onNodeOut : function(n, dd, e, data){
18680         
18681     },
18682
18683     /**
18684      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18685      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18686      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18687      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18688      * {@link #getTargetFromEvent} for this node)
18689      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18690      * @param {Event} e The event
18691      * @param {Object} data An object containing arbitrary data supplied by the drag source
18692      * @return {Boolean} True if the drop was valid, else false
18693      */
18694     onNodeDrop : function(n, dd, e, data){
18695         return false;
18696     },
18697
18698     /**
18699      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18700      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18701      * it should be overridden to provide the proper feedback if necessary.
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 {String} status The CSS class that communicates the drop status back to the source so that the
18706      * underlying {@link Roo.dd.StatusProxy} can be updated
18707      */
18708     onContainerOver : function(dd, e, data){
18709         return this.dropNotAllowed;
18710     },
18711
18712     /**
18713      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18714      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18715      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18716      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18717      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18718      * @param {Event} e The event
18719      * @param {Object} data An object containing arbitrary data supplied by the drag source
18720      * @return {Boolean} True if the drop was valid, else false
18721      */
18722     onContainerDrop : function(dd, e, data){
18723         return false;
18724     },
18725
18726     /**
18727      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18728      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18729      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18730      * you should override this method and provide a custom implementation.
18731      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18732      * @param {Event} e The event
18733      * @param {Object} data An object containing arbitrary data supplied by the drag source
18734      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18735      * underlying {@link Roo.dd.StatusProxy} can be updated
18736      */
18737     notifyEnter : function(dd, e, data){
18738         return this.dropNotAllowed;
18739     },
18740
18741     /**
18742      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18743      * This method will be called on every mouse movement while the drag source is over the drop zone.
18744      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18745      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18746      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18747      * registered node, it will call {@link #onContainerOver}.
18748      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18749      * @param {Event} e The event
18750      * @param {Object} data An object containing arbitrary data supplied by the drag source
18751      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18752      * underlying {@link Roo.dd.StatusProxy} can be updated
18753      */
18754     notifyOver : function(dd, e, data){
18755         var n = this.getTargetFromEvent(e);
18756         if(!n){ // not over valid drop target
18757             if(this.lastOverNode){
18758                 this.onNodeOut(this.lastOverNode, dd, e, data);
18759                 this.lastOverNode = null;
18760             }
18761             return this.onContainerOver(dd, e, data);
18762         }
18763         if(this.lastOverNode != n){
18764             if(this.lastOverNode){
18765                 this.onNodeOut(this.lastOverNode, dd, e, data);
18766             }
18767             this.onNodeEnter(n, dd, e, data);
18768             this.lastOverNode = n;
18769         }
18770         return this.onNodeOver(n, dd, e, data);
18771     },
18772
18773     /**
18774      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18775      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18776      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18777      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18778      * @param {Event} e The event
18779      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18780      */
18781     notifyOut : function(dd, e, data){
18782         if(this.lastOverNode){
18783             this.onNodeOut(this.lastOverNode, dd, e, data);
18784             this.lastOverNode = null;
18785         }
18786     },
18787
18788     /**
18789      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18790      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18791      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18792      * otherwise it will call {@link #onContainerDrop}.
18793      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18794      * @param {Event} e The event
18795      * @param {Object} data An object containing arbitrary data supplied by the drag source
18796      * @return {Boolean} True if the drop was valid, else false
18797      */
18798     notifyDrop : function(dd, e, data){
18799         if(this.lastOverNode){
18800             this.onNodeOut(this.lastOverNode, dd, e, data);
18801             this.lastOverNode = null;
18802         }
18803         var n = this.getTargetFromEvent(e);
18804         return n ?
18805             this.onNodeDrop(n, dd, e, data) :
18806             this.onContainerDrop(dd, e, data);
18807     },
18808
18809     // private
18810     triggerCacheRefresh : function(){
18811         Roo.dd.DDM.refreshCache(this.groups);
18812     }  
18813 });/*
18814  * Based on:
18815  * Ext JS Library 1.1.1
18816  * Copyright(c) 2006-2007, Ext JS, LLC.
18817  *
18818  * Originally Released Under LGPL - original licence link has changed is not relivant.
18819  *
18820  * Fork - LGPL
18821  * <script type="text/javascript">
18822  */
18823
18824
18825 /**
18826  * @class Roo.data.SortTypes
18827  * @singleton
18828  * Defines the default sorting (casting?) comparison functions used when sorting data.
18829  */
18830 Roo.data.SortTypes = {
18831     /**
18832      * Default sort that does nothing
18833      * @param {Mixed} s The value being converted
18834      * @return {Mixed} The comparison value
18835      */
18836     none : function(s){
18837         return s;
18838     },
18839     
18840     /**
18841      * The regular expression used to strip tags
18842      * @type {RegExp}
18843      * @property
18844      */
18845     stripTagsRE : /<\/?[^>]+>/gi,
18846     
18847     /**
18848      * Strips all HTML tags to sort on text only
18849      * @param {Mixed} s The value being converted
18850      * @return {String} The comparison value
18851      */
18852     asText : function(s){
18853         return String(s).replace(this.stripTagsRE, "");
18854     },
18855     
18856     /**
18857      * Strips all HTML tags to sort on text only - Case insensitive
18858      * @param {Mixed} s The value being converted
18859      * @return {String} The comparison value
18860      */
18861     asUCText : function(s){
18862         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18863     },
18864     
18865     /**
18866      * Case insensitive string
18867      * @param {Mixed} s The value being converted
18868      * @return {String} The comparison value
18869      */
18870     asUCString : function(s) {
18871         return String(s).toUpperCase();
18872     },
18873     
18874     /**
18875      * Date sorting
18876      * @param {Mixed} s The value being converted
18877      * @return {Number} The comparison value
18878      */
18879     asDate : function(s) {
18880         if(!s){
18881             return 0;
18882         }
18883         if(s instanceof Date){
18884             return s.getTime();
18885         }
18886         return Date.parse(String(s));
18887     },
18888     
18889     /**
18890      * Float sorting
18891      * @param {Mixed} s The value being converted
18892      * @return {Float} The comparison value
18893      */
18894     asFloat : function(s) {
18895         var val = parseFloat(String(s).replace(/,/g, ""));
18896         if(isNaN(val)) val = 0;
18897         return val;
18898     },
18899     
18900     /**
18901      * Integer sorting
18902      * @param {Mixed} s The value being converted
18903      * @return {Number} The comparison value
18904      */
18905     asInt : function(s) {
18906         var val = parseInt(String(s).replace(/,/g, ""));
18907         if(isNaN(val)) val = 0;
18908         return val;
18909     }
18910 };/*
18911  * Based on:
18912  * Ext JS Library 1.1.1
18913  * Copyright(c) 2006-2007, Ext JS, LLC.
18914  *
18915  * Originally Released Under LGPL - original licence link has changed is not relivant.
18916  *
18917  * Fork - LGPL
18918  * <script type="text/javascript">
18919  */
18920
18921 /**
18922 * @class Roo.data.Record
18923  * Instances of this class encapsulate both record <em>definition</em> information, and record
18924  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18925  * to access Records cached in an {@link Roo.data.Store} object.<br>
18926  * <p>
18927  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18928  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18929  * objects.<br>
18930  * <p>
18931  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18932  * @constructor
18933  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18934  * {@link #create}. The parameters are the same.
18935  * @param {Array} data An associative Array of data values keyed by the field name.
18936  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18937  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18938  * not specified an integer id is generated.
18939  */
18940 Roo.data.Record = function(data, id){
18941     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18942     this.data = data;
18943 };
18944
18945 /**
18946  * Generate a constructor for a specific record layout.
18947  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18948  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18949  * Each field definition object may contain the following properties: <ul>
18950  * <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,
18951  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18952  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18953  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18954  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18955  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18956  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18957  * this may be omitted.</p></li>
18958  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18959  * <ul><li>auto (Default, implies no conversion)</li>
18960  * <li>string</li>
18961  * <li>int</li>
18962  * <li>float</li>
18963  * <li>boolean</li>
18964  * <li>date</li></ul></p></li>
18965  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18966  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18967  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18968  * by the Reader into an object that will be stored in the Record. It is passed the
18969  * following parameters:<ul>
18970  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18971  * </ul></p></li>
18972  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18973  * </ul>
18974  * <br>usage:<br><pre><code>
18975 var TopicRecord = Roo.data.Record.create(
18976     {name: 'title', mapping: 'topic_title'},
18977     {name: 'author', mapping: 'username'},
18978     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18979     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18980     {name: 'lastPoster', mapping: 'user2'},
18981     {name: 'excerpt', mapping: 'post_text'}
18982 );
18983
18984 var myNewRecord = new TopicRecord({
18985     title: 'Do my job please',
18986     author: 'noobie',
18987     totalPosts: 1,
18988     lastPost: new Date(),
18989     lastPoster: 'Animal',
18990     excerpt: 'No way dude!'
18991 });
18992 myStore.add(myNewRecord);
18993 </code></pre>
18994  * @method create
18995  * @static
18996  */
18997 Roo.data.Record.create = function(o){
18998     var f = function(){
18999         f.superclass.constructor.apply(this, arguments);
19000     };
19001     Roo.extend(f, Roo.data.Record);
19002     var p = f.prototype;
19003     p.fields = new Roo.util.MixedCollection(false, function(field){
19004         return field.name;
19005     });
19006     for(var i = 0, len = o.length; i < len; i++){
19007         p.fields.add(new Roo.data.Field(o[i]));
19008     }
19009     f.getField = function(name){
19010         return p.fields.get(name);  
19011     };
19012     return f;
19013 };
19014
19015 Roo.data.Record.AUTO_ID = 1000;
19016 Roo.data.Record.EDIT = 'edit';
19017 Roo.data.Record.REJECT = 'reject';
19018 Roo.data.Record.COMMIT = 'commit';
19019
19020 Roo.data.Record.prototype = {
19021     /**
19022      * Readonly flag - true if this record has been modified.
19023      * @type Boolean
19024      */
19025     dirty : false,
19026     editing : false,
19027     error: null,
19028     modified: null,
19029
19030     // private
19031     join : function(store){
19032         this.store = store;
19033     },
19034
19035     /**
19036      * Set the named field to the specified value.
19037      * @param {String} name The name of the field to set.
19038      * @param {Object} value The value to set the field to.
19039      */
19040     set : function(name, value){
19041         if(this.data[name] == value){
19042             return;
19043         }
19044         this.dirty = true;
19045         if(!this.modified){
19046             this.modified = {};
19047         }
19048         if(typeof this.modified[name] == 'undefined'){
19049             this.modified[name] = this.data[name];
19050         }
19051         this.data[name] = value;
19052         if(!this.editing){
19053             this.store.afterEdit(this);
19054         }       
19055     },
19056
19057     /**
19058      * Get the value of the named field.
19059      * @param {String} name The name of the field to get the value of.
19060      * @return {Object} The value of the field.
19061      */
19062     get : function(name){
19063         return this.data[name]; 
19064     },
19065
19066     // private
19067     beginEdit : function(){
19068         this.editing = true;
19069         this.modified = {}; 
19070     },
19071
19072     // private
19073     cancelEdit : function(){
19074         this.editing = false;
19075         delete this.modified;
19076     },
19077
19078     // private
19079     endEdit : function(){
19080         this.editing = false;
19081         if(this.dirty && this.store){
19082             this.store.afterEdit(this);
19083         }
19084     },
19085
19086     /**
19087      * Usually called by the {@link Roo.data.Store} which owns the Record.
19088      * Rejects all changes made to the Record since either creation, or the last commit operation.
19089      * Modified fields are reverted to their original values.
19090      * <p>
19091      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19092      * of reject operations.
19093      */
19094     reject : function(){
19095         var m = this.modified;
19096         for(var n in m){
19097             if(typeof m[n] != "function"){
19098                 this.data[n] = m[n];
19099             }
19100         }
19101         this.dirty = false;
19102         delete this.modified;
19103         this.editing = false;
19104         if(this.store){
19105             this.store.afterReject(this);
19106         }
19107     },
19108
19109     /**
19110      * Usually called by the {@link Roo.data.Store} which owns the Record.
19111      * Commits all changes made to the Record since either creation, or the last commit operation.
19112      * <p>
19113      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19114      * of commit operations.
19115      */
19116     commit : function(){
19117         this.dirty = false;
19118         delete this.modified;
19119         this.editing = false;
19120         if(this.store){
19121             this.store.afterCommit(this);
19122         }
19123     },
19124
19125     // private
19126     hasError : function(){
19127         return this.error != null;
19128     },
19129
19130     // private
19131     clearError : function(){
19132         this.error = null;
19133     },
19134
19135     /**
19136      * Creates a copy of this record.
19137      * @param {String} id (optional) A new record id if you don't want to use this record's id
19138      * @return {Record}
19139      */
19140     copy : function(newId) {
19141         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19142     }
19143 };/*
19144  * Based on:
19145  * Ext JS Library 1.1.1
19146  * Copyright(c) 2006-2007, Ext JS, LLC.
19147  *
19148  * Originally Released Under LGPL - original licence link has changed is not relivant.
19149  *
19150  * Fork - LGPL
19151  * <script type="text/javascript">
19152  */
19153
19154
19155
19156 /**
19157  * @class Roo.data.Store
19158  * @extends Roo.util.Observable
19159  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19160  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19161  * <p>
19162  * 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
19163  * has no knowledge of the format of the data returned by the Proxy.<br>
19164  * <p>
19165  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19166  * instances from the data object. These records are cached and made available through accessor functions.
19167  * @constructor
19168  * Creates a new Store.
19169  * @param {Object} config A config object containing the objects needed for the Store to access data,
19170  * and read the data into Records.
19171  */
19172 Roo.data.Store = function(config){
19173     this.data = new Roo.util.MixedCollection(false);
19174     this.data.getKey = function(o){
19175         return o.id;
19176     };
19177     this.baseParams = {};
19178     // private
19179     this.paramNames = {
19180         "start" : "start",
19181         "limit" : "limit",
19182         "sort" : "sort",
19183         "dir" : "dir",
19184         "multisort" : "_multisort"
19185     };
19186
19187     if(config && config.data){
19188         this.inlineData = config.data;
19189         delete config.data;
19190     }
19191
19192     Roo.apply(this, config);
19193     
19194     if(this.reader){ // reader passed
19195         this.reader = Roo.factory(this.reader, Roo.data);
19196         this.reader.xmodule = this.xmodule || false;
19197         if(!this.recordType){
19198             this.recordType = this.reader.recordType;
19199         }
19200         if(this.reader.onMetaChange){
19201             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19202         }
19203     }
19204
19205     if(this.recordType){
19206         this.fields = this.recordType.prototype.fields;
19207     }
19208     this.modified = [];
19209
19210     this.addEvents({
19211         /**
19212          * @event datachanged
19213          * Fires when the data cache has changed, and a widget which is using this Store
19214          * as a Record cache should refresh its view.
19215          * @param {Store} this
19216          */
19217         datachanged : true,
19218         /**
19219          * @event metachange
19220          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19221          * @param {Store} this
19222          * @param {Object} meta The JSON metadata
19223          */
19224         metachange : true,
19225         /**
19226          * @event add
19227          * Fires when Records have been added to the Store
19228          * @param {Store} this
19229          * @param {Roo.data.Record[]} records The array of Records added
19230          * @param {Number} index The index at which the record(s) were added
19231          */
19232         add : true,
19233         /**
19234          * @event remove
19235          * Fires when a Record has been removed from the Store
19236          * @param {Store} this
19237          * @param {Roo.data.Record} record The Record that was removed
19238          * @param {Number} index The index at which the record was removed
19239          */
19240         remove : true,
19241         /**
19242          * @event update
19243          * Fires when a Record has been updated
19244          * @param {Store} this
19245          * @param {Roo.data.Record} record The Record that was updated
19246          * @param {String} operation The update operation being performed.  Value may be one of:
19247          * <pre><code>
19248  Roo.data.Record.EDIT
19249  Roo.data.Record.REJECT
19250  Roo.data.Record.COMMIT
19251          * </code></pre>
19252          */
19253         update : true,
19254         /**
19255          * @event clear
19256          * Fires when the data cache has been cleared.
19257          * @param {Store} this
19258          */
19259         clear : true,
19260         /**
19261          * @event beforeload
19262          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19263          * the load action will be canceled.
19264          * @param {Store} this
19265          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19266          */
19267         beforeload : true,
19268         /**
19269          * @event load
19270          * Fires after a new set of Records has been loaded.
19271          * @param {Store} this
19272          * @param {Roo.data.Record[]} records The Records that were loaded
19273          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19274          */
19275         load : true,
19276         /**
19277          * @event loadexception
19278          * Fires if an exception occurs in the Proxy during loading.
19279          * Called with the signature of the Proxy's "loadexception" event.
19280          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19281          * 
19282          * @param {Proxy} 
19283          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19284          * @param {Object} load options 
19285          * @param {Object} jsonData from your request (normally this contains the Exception)
19286          */
19287         loadexception : true
19288     });
19289     
19290     if(this.proxy){
19291         this.proxy = Roo.factory(this.proxy, Roo.data);
19292         this.proxy.xmodule = this.xmodule || false;
19293         this.relayEvents(this.proxy,  ["loadexception"]);
19294     }
19295     this.sortToggle = {};
19296     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19297
19298     Roo.data.Store.superclass.constructor.call(this);
19299
19300     if(this.inlineData){
19301         this.loadData(this.inlineData);
19302         delete this.inlineData;
19303     }
19304 };
19305 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19306      /**
19307     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19308     * without a remote query - used by combo/forms at present.
19309     */
19310     
19311     /**
19312     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19313     */
19314     /**
19315     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19316     */
19317     /**
19318     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19319     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19320     */
19321     /**
19322     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19323     * on any HTTP request
19324     */
19325     /**
19326     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19327     */
19328     /**
19329     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19330     */
19331     multiSort: false,
19332     /**
19333     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19334     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19335     */
19336     remoteSort : false,
19337
19338     /**
19339     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19340      * loaded or when a record is removed. (defaults to false).
19341     */
19342     pruneModifiedRecords : false,
19343
19344     // private
19345     lastOptions : null,
19346
19347     /**
19348      * Add Records to the Store and fires the add event.
19349      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19350      */
19351     add : function(records){
19352         records = [].concat(records);
19353         for(var i = 0, len = records.length; i < len; i++){
19354             records[i].join(this);
19355         }
19356         var index = this.data.length;
19357         this.data.addAll(records);
19358         this.fireEvent("add", this, records, index);
19359     },
19360
19361     /**
19362      * Remove a Record from the Store and fires the remove event.
19363      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19364      */
19365     remove : function(record){
19366         var index = this.data.indexOf(record);
19367         this.data.removeAt(index);
19368         if(this.pruneModifiedRecords){
19369             this.modified.remove(record);
19370         }
19371         this.fireEvent("remove", this, record, index);
19372     },
19373
19374     /**
19375      * Remove all Records from the Store and fires the clear event.
19376      */
19377     removeAll : function(){
19378         this.data.clear();
19379         if(this.pruneModifiedRecords){
19380             this.modified = [];
19381         }
19382         this.fireEvent("clear", this);
19383     },
19384
19385     /**
19386      * Inserts Records to the Store at the given index and fires the add event.
19387      * @param {Number} index The start index at which to insert the passed Records.
19388      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19389      */
19390     insert : function(index, records){
19391         records = [].concat(records);
19392         for(var i = 0, len = records.length; i < len; i++){
19393             this.data.insert(index, records[i]);
19394             records[i].join(this);
19395         }
19396         this.fireEvent("add", this, records, index);
19397     },
19398
19399     /**
19400      * Get the index within the cache of the passed Record.
19401      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19402      * @return {Number} The index of the passed Record. Returns -1 if not found.
19403      */
19404     indexOf : function(record){
19405         return this.data.indexOf(record);
19406     },
19407
19408     /**
19409      * Get the index within the cache of the Record with the passed id.
19410      * @param {String} id The id of the Record to find.
19411      * @return {Number} The index of the Record. Returns -1 if not found.
19412      */
19413     indexOfId : function(id){
19414         return this.data.indexOfKey(id);
19415     },
19416
19417     /**
19418      * Get the Record with the specified id.
19419      * @param {String} id The id of the Record to find.
19420      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19421      */
19422     getById : function(id){
19423         return this.data.key(id);
19424     },
19425
19426     /**
19427      * Get the Record at the specified index.
19428      * @param {Number} index The index of the Record to find.
19429      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19430      */
19431     getAt : function(index){
19432         return this.data.itemAt(index);
19433     },
19434
19435     /**
19436      * Returns a range of Records between specified indices.
19437      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19438      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19439      * @return {Roo.data.Record[]} An array of Records
19440      */
19441     getRange : function(start, end){
19442         return this.data.getRange(start, end);
19443     },
19444
19445     // private
19446     storeOptions : function(o){
19447         o = Roo.apply({}, o);
19448         delete o.callback;
19449         delete o.scope;
19450         this.lastOptions = o;
19451     },
19452
19453     /**
19454      * Loads the Record cache from the configured Proxy using the configured Reader.
19455      * <p>
19456      * If using remote paging, then the first load call must specify the <em>start</em>
19457      * and <em>limit</em> properties in the options.params property to establish the initial
19458      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19459      * <p>
19460      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19461      * and this call will return before the new data has been loaded. Perform any post-processing
19462      * in a callback function, or in a "load" event handler.</strong>
19463      * <p>
19464      * @param {Object} options An object containing properties which control loading options:<ul>
19465      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19466      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19467      * passed the following arguments:<ul>
19468      * <li>r : Roo.data.Record[]</li>
19469      * <li>options: Options object from the load call</li>
19470      * <li>success: Boolean success indicator</li></ul></li>
19471      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19472      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19473      * </ul>
19474      */
19475     load : function(options){
19476         options = options || {};
19477         if(this.fireEvent("beforeload", this, options) !== false){
19478             this.storeOptions(options);
19479             var p = Roo.apply(options.params || {}, this.baseParams);
19480             // if meta was not loaded from remote source.. try requesting it.
19481             if (!this.reader.metaFromRemote) {
19482                 p._requestMeta = 1;
19483             }
19484             if(this.sortInfo && this.remoteSort){
19485                 var pn = this.paramNames;
19486                 p[pn["sort"]] = this.sortInfo.field;
19487                 p[pn["dir"]] = this.sortInfo.direction;
19488             }
19489             if (this.multiSort) {
19490                 var pn = this.paramNames;
19491                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19492             }
19493             
19494             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19495         }
19496     },
19497
19498     /**
19499      * Reloads the Record cache from the configured Proxy using the configured Reader and
19500      * the options from the last load operation performed.
19501      * @param {Object} options (optional) An object containing properties which may override the options
19502      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19503      * the most recently used options are reused).
19504      */
19505     reload : function(options){
19506         this.load(Roo.applyIf(options||{}, this.lastOptions));
19507     },
19508
19509     // private
19510     // Called as a callback by the Reader during a load operation.
19511     loadRecords : function(o, options, success){
19512         if(!o || success === false){
19513             if(success !== false){
19514                 this.fireEvent("load", this, [], options);
19515             }
19516             if(options.callback){
19517                 options.callback.call(options.scope || this, [], options, false);
19518             }
19519             return;
19520         }
19521         // if data returned failure - throw an exception.
19522         if (o.success === false) {
19523             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19524             return;
19525         }
19526         var r = o.records, t = o.totalRecords || r.length;
19527         if(!options || options.add !== true){
19528             if(this.pruneModifiedRecords){
19529                 this.modified = [];
19530             }
19531             for(var i = 0, len = r.length; i < len; i++){
19532                 r[i].join(this);
19533             }
19534             if(this.snapshot){
19535                 this.data = this.snapshot;
19536                 delete this.snapshot;
19537             }
19538             this.data.clear();
19539             this.data.addAll(r);
19540             this.totalLength = t;
19541             this.applySort();
19542             this.fireEvent("datachanged", this);
19543         }else{
19544             this.totalLength = Math.max(t, this.data.length+r.length);
19545             this.add(r);
19546         }
19547         this.fireEvent("load", this, r, options);
19548         if(options.callback){
19549             options.callback.call(options.scope || this, r, options, true);
19550         }
19551     },
19552
19553     /**
19554      * Loads data from a passed data block. A Reader which understands the format of the data
19555      * must have been configured in the constructor.
19556      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19557      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19558      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19559      */
19560     loadData : function(o, append){
19561         var r = this.reader.readRecords(o);
19562         this.loadRecords(r, {add: append}, true);
19563     },
19564
19565     /**
19566      * Gets the number of cached records.
19567      * <p>
19568      * <em>If using paging, this may not be the total size of the dataset. If the data object
19569      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19570      * the data set size</em>
19571      */
19572     getCount : function(){
19573         return this.data.length || 0;
19574     },
19575
19576     /**
19577      * Gets the total number of records in the dataset as returned by the server.
19578      * <p>
19579      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19580      * the dataset size</em>
19581      */
19582     getTotalCount : function(){
19583         return this.totalLength || 0;
19584     },
19585
19586     /**
19587      * Returns the sort state of the Store as an object with two properties:
19588      * <pre><code>
19589  field {String} The name of the field by which the Records are sorted
19590  direction {String} The sort order, "ASC" or "DESC"
19591      * </code></pre>
19592      */
19593     getSortState : function(){
19594         return this.sortInfo;
19595     },
19596
19597     // private
19598     applySort : function(){
19599         if(this.sortInfo && !this.remoteSort){
19600             var s = this.sortInfo, f = s.field;
19601             var st = this.fields.get(f).sortType;
19602             var fn = function(r1, r2){
19603                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19604                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19605             };
19606             this.data.sort(s.direction, fn);
19607             if(this.snapshot && this.snapshot != this.data){
19608                 this.snapshot.sort(s.direction, fn);
19609             }
19610         }
19611     },
19612
19613     /**
19614      * Sets the default sort column and order to be used by the next load operation.
19615      * @param {String} fieldName The name of the field to sort by.
19616      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19617      */
19618     setDefaultSort : function(field, dir){
19619         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19620     },
19621
19622     /**
19623      * Sort the Records.
19624      * If remote sorting is used, the sort is performed on the server, and the cache is
19625      * reloaded. If local sorting is used, the cache is sorted internally.
19626      * @param {String} fieldName The name of the field to sort by.
19627      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19628      */
19629     sort : function(fieldName, dir){
19630         var f = this.fields.get(fieldName);
19631         if(!dir){
19632             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19633             
19634             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19635                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19636             }else{
19637                 dir = f.sortDir;
19638             }
19639         }
19640         this.sortToggle[f.name] = dir;
19641         this.sortInfo = {field: f.name, direction: dir};
19642         if(!this.remoteSort){
19643             this.applySort();
19644             this.fireEvent("datachanged", this);
19645         }else{
19646             this.load(this.lastOptions);
19647         }
19648     },
19649
19650     /**
19651      * Calls the specified function for each of the Records in the cache.
19652      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19653      * Returning <em>false</em> aborts and exits the iteration.
19654      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19655      */
19656     each : function(fn, scope){
19657         this.data.each(fn, scope);
19658     },
19659
19660     /**
19661      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19662      * (e.g., during paging).
19663      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19664      */
19665     getModifiedRecords : function(){
19666         return this.modified;
19667     },
19668
19669     // private
19670     createFilterFn : function(property, value, anyMatch){
19671         if(!value.exec){ // not a regex
19672             value = String(value);
19673             if(value.length == 0){
19674                 return false;
19675             }
19676             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19677         }
19678         return function(r){
19679             return value.test(r.data[property]);
19680         };
19681     },
19682
19683     /**
19684      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19685      * @param {String} property A field on your records
19686      * @param {Number} start The record index to start at (defaults to 0)
19687      * @param {Number} end The last record index to include (defaults to length - 1)
19688      * @return {Number} The sum
19689      */
19690     sum : function(property, start, end){
19691         var rs = this.data.items, v = 0;
19692         start = start || 0;
19693         end = (end || end === 0) ? end : rs.length-1;
19694
19695         for(var i = start; i <= end; i++){
19696             v += (rs[i].data[property] || 0);
19697         }
19698         return v;
19699     },
19700
19701     /**
19702      * Filter the records by a specified property.
19703      * @param {String} field A field on your records
19704      * @param {String/RegExp} value Either a string that the field
19705      * should start with or a RegExp to test against the field
19706      * @param {Boolean} anyMatch True to match any part not just the beginning
19707      */
19708     filter : function(property, value, anyMatch){
19709         var fn = this.createFilterFn(property, value, anyMatch);
19710         return fn ? this.filterBy(fn) : this.clearFilter();
19711     },
19712
19713     /**
19714      * Filter by a function. The specified function will be called with each
19715      * record in this data source. If the function returns true the record is included,
19716      * otherwise it is filtered.
19717      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19718      * @param {Object} scope (optional) The scope of the function (defaults to this)
19719      */
19720     filterBy : function(fn, scope){
19721         this.snapshot = this.snapshot || this.data;
19722         this.data = this.queryBy(fn, scope||this);
19723         this.fireEvent("datachanged", this);
19724     },
19725
19726     /**
19727      * Query the records by a specified property.
19728      * @param {String} field A field on your records
19729      * @param {String/RegExp} value Either a string that the field
19730      * should start with or a RegExp to test against the field
19731      * @param {Boolean} anyMatch True to match any part not just the beginning
19732      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19733      */
19734     query : function(property, value, anyMatch){
19735         var fn = this.createFilterFn(property, value, anyMatch);
19736         return fn ? this.queryBy(fn) : this.data.clone();
19737     },
19738
19739     /**
19740      * Query by a function. The specified function will be called with each
19741      * record in this data source. If the function returns true the record is included
19742      * in the results.
19743      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19744      * @param {Object} scope (optional) The scope of the function (defaults to this)
19745       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19746      **/
19747     queryBy : function(fn, scope){
19748         var data = this.snapshot || this.data;
19749         return data.filterBy(fn, scope||this);
19750     },
19751
19752     /**
19753      * Collects unique values for a particular dataIndex from this store.
19754      * @param {String} dataIndex The property to collect
19755      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19756      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19757      * @return {Array} An array of the unique values
19758      **/
19759     collect : function(dataIndex, allowNull, bypassFilter){
19760         var d = (bypassFilter === true && this.snapshot) ?
19761                 this.snapshot.items : this.data.items;
19762         var v, sv, r = [], l = {};
19763         for(var i = 0, len = d.length; i < len; i++){
19764             v = d[i].data[dataIndex];
19765             sv = String(v);
19766             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19767                 l[sv] = true;
19768                 r[r.length] = v;
19769             }
19770         }
19771         return r;
19772     },
19773
19774     /**
19775      * Revert to a view of the Record cache with no filtering applied.
19776      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19777      */
19778     clearFilter : function(suppressEvent){
19779         if(this.snapshot && this.snapshot != this.data){
19780             this.data = this.snapshot;
19781             delete this.snapshot;
19782             if(suppressEvent !== true){
19783                 this.fireEvent("datachanged", this);
19784             }
19785         }
19786     },
19787
19788     // private
19789     afterEdit : function(record){
19790         if(this.modified.indexOf(record) == -1){
19791             this.modified.push(record);
19792         }
19793         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19794     },
19795
19796     // private
19797     afterReject : function(record){
19798         this.modified.remove(record);
19799         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19800     },
19801
19802     // private
19803     afterCommit : function(record){
19804         this.modified.remove(record);
19805         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19806     },
19807
19808     /**
19809      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19810      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19811      */
19812     commitChanges : function(){
19813         var m = this.modified.slice(0);
19814         this.modified = [];
19815         for(var i = 0, len = m.length; i < len; i++){
19816             m[i].commit();
19817         }
19818     },
19819
19820     /**
19821      * Cancel outstanding changes on all changed records.
19822      */
19823     rejectChanges : function(){
19824         var m = this.modified.slice(0);
19825         this.modified = [];
19826         for(var i = 0, len = m.length; i < len; i++){
19827             m[i].reject();
19828         }
19829     },
19830
19831     onMetaChange : function(meta, rtype, o){
19832         this.recordType = rtype;
19833         this.fields = rtype.prototype.fields;
19834         delete this.snapshot;
19835         this.sortInfo = meta.sortInfo || this.sortInfo;
19836         this.modified = [];
19837         this.fireEvent('metachange', this, this.reader.meta);
19838     }
19839 });/*
19840  * Based on:
19841  * Ext JS Library 1.1.1
19842  * Copyright(c) 2006-2007, Ext JS, LLC.
19843  *
19844  * Originally Released Under LGPL - original licence link has changed is not relivant.
19845  *
19846  * Fork - LGPL
19847  * <script type="text/javascript">
19848  */
19849
19850 /**
19851  * @class Roo.data.SimpleStore
19852  * @extends Roo.data.Store
19853  * Small helper class to make creating Stores from Array data easier.
19854  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19855  * @cfg {Array} fields An array of field definition objects, or field name strings.
19856  * @cfg {Array} data The multi-dimensional array of data
19857  * @constructor
19858  * @param {Object} config
19859  */
19860 Roo.data.SimpleStore = function(config){
19861     Roo.data.SimpleStore.superclass.constructor.call(this, {
19862         isLocal : true,
19863         reader: new Roo.data.ArrayReader({
19864                 id: config.id
19865             },
19866             Roo.data.Record.create(config.fields)
19867         ),
19868         proxy : new Roo.data.MemoryProxy(config.data)
19869     });
19870     this.load();
19871 };
19872 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19873  * Based on:
19874  * Ext JS Library 1.1.1
19875  * Copyright(c) 2006-2007, Ext JS, LLC.
19876  *
19877  * Originally Released Under LGPL - original licence link has changed is not relivant.
19878  *
19879  * Fork - LGPL
19880  * <script type="text/javascript">
19881  */
19882
19883 /**
19884 /**
19885  * @extends Roo.data.Store
19886  * @class Roo.data.JsonStore
19887  * Small helper class to make creating Stores for JSON data easier. <br/>
19888 <pre><code>
19889 var store = new Roo.data.JsonStore({
19890     url: 'get-images.php',
19891     root: 'images',
19892     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19893 });
19894 </code></pre>
19895  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19896  * JsonReader and HttpProxy (unless inline data is provided).</b>
19897  * @cfg {Array} fields An array of field definition objects, or field name strings.
19898  * @constructor
19899  * @param {Object} config
19900  */
19901 Roo.data.JsonStore = function(c){
19902     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19903         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19904         reader: new Roo.data.JsonReader(c, c.fields)
19905     }));
19906 };
19907 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19908  * Based on:
19909  * Ext JS Library 1.1.1
19910  * Copyright(c) 2006-2007, Ext JS, LLC.
19911  *
19912  * Originally Released Under LGPL - original licence link has changed is not relivant.
19913  *
19914  * Fork - LGPL
19915  * <script type="text/javascript">
19916  */
19917
19918  
19919 Roo.data.Field = function(config){
19920     if(typeof config == "string"){
19921         config = {name: config};
19922     }
19923     Roo.apply(this, config);
19924     
19925     if(!this.type){
19926         this.type = "auto";
19927     }
19928     
19929     var st = Roo.data.SortTypes;
19930     // named sortTypes are supported, here we look them up
19931     if(typeof this.sortType == "string"){
19932         this.sortType = st[this.sortType];
19933     }
19934     
19935     // set default sortType for strings and dates
19936     if(!this.sortType){
19937         switch(this.type){
19938             case "string":
19939                 this.sortType = st.asUCString;
19940                 break;
19941             case "date":
19942                 this.sortType = st.asDate;
19943                 break;
19944             default:
19945                 this.sortType = st.none;
19946         }
19947     }
19948
19949     // define once
19950     var stripRe = /[\$,%]/g;
19951
19952     // prebuilt conversion function for this field, instead of
19953     // switching every time we're reading a value
19954     if(!this.convert){
19955         var cv, dateFormat = this.dateFormat;
19956         switch(this.type){
19957             case "":
19958             case "auto":
19959             case undefined:
19960                 cv = function(v){ return v; };
19961                 break;
19962             case "string":
19963                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19964                 break;
19965             case "int":
19966                 cv = function(v){
19967                     return v !== undefined && v !== null && v !== '' ?
19968                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19969                     };
19970                 break;
19971             case "float":
19972                 cv = function(v){
19973                     return v !== undefined && v !== null && v !== '' ?
19974                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19975                     };
19976                 break;
19977             case "bool":
19978             case "boolean":
19979                 cv = function(v){ return v === true || v === "true" || v == 1; };
19980                 break;
19981             case "date":
19982                 cv = function(v){
19983                     if(!v){
19984                         return '';
19985                     }
19986                     if(v instanceof Date){
19987                         return v;
19988                     }
19989                     if(dateFormat){
19990                         if(dateFormat == "timestamp"){
19991                             return new Date(v*1000);
19992                         }
19993                         return Date.parseDate(v, dateFormat);
19994                     }
19995                     var parsed = Date.parse(v);
19996                     return parsed ? new Date(parsed) : null;
19997                 };
19998              break;
19999             
20000         }
20001         this.convert = cv;
20002     }
20003 };
20004
20005 Roo.data.Field.prototype = {
20006     dateFormat: null,
20007     defaultValue: "",
20008     mapping: null,
20009     sortType : null,
20010     sortDir : "ASC"
20011 };/*
20012  * Based on:
20013  * Ext JS Library 1.1.1
20014  * Copyright(c) 2006-2007, Ext JS, LLC.
20015  *
20016  * Originally Released Under LGPL - original licence link has changed is not relivant.
20017  *
20018  * Fork - LGPL
20019  * <script type="text/javascript">
20020  */
20021  
20022 // Base class for reading structured data from a data source.  This class is intended to be
20023 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20024
20025 /**
20026  * @class Roo.data.DataReader
20027  * Base class for reading structured data from a data source.  This class is intended to be
20028  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20029  */
20030
20031 Roo.data.DataReader = function(meta, recordType){
20032     
20033     this.meta = meta;
20034     
20035     this.recordType = recordType instanceof Array ? 
20036         Roo.data.Record.create(recordType) : recordType;
20037 };
20038
20039 Roo.data.DataReader.prototype = {
20040      /**
20041      * Create an empty record
20042      * @param {Object} data (optional) - overlay some values
20043      * @return {Roo.data.Record} record created.
20044      */
20045     newRow :  function(d) {
20046         var da =  {};
20047         this.recordType.prototype.fields.each(function(c) {
20048             switch( c.type) {
20049                 case 'int' : da[c.name] = 0; break;
20050                 case 'date' : da[c.name] = new Date(); break;
20051                 case 'float' : da[c.name] = 0.0; break;
20052                 case 'boolean' : da[c.name] = false; break;
20053                 default : da[c.name] = ""; break;
20054             }
20055             
20056         });
20057         return new this.recordType(Roo.apply(da, d));
20058     }
20059     
20060 };/*
20061  * Based on:
20062  * Ext JS Library 1.1.1
20063  * Copyright(c) 2006-2007, Ext JS, LLC.
20064  *
20065  * Originally Released Under LGPL - original licence link has changed is not relivant.
20066  *
20067  * Fork - LGPL
20068  * <script type="text/javascript">
20069  */
20070
20071 /**
20072  * @class Roo.data.DataProxy
20073  * @extends Roo.data.Observable
20074  * This class is an abstract base class for implementations which provide retrieval of
20075  * unformatted data objects.<br>
20076  * <p>
20077  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20078  * (of the appropriate type which knows how to parse the data object) to provide a block of
20079  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20080  * <p>
20081  * Custom implementations must implement the load method as described in
20082  * {@link Roo.data.HttpProxy#load}.
20083  */
20084 Roo.data.DataProxy = function(){
20085     this.addEvents({
20086         /**
20087          * @event beforeload
20088          * Fires before a network request is made to retrieve a data object.
20089          * @param {Object} This DataProxy object.
20090          * @param {Object} params The params parameter to the load function.
20091          */
20092         beforeload : true,
20093         /**
20094          * @event load
20095          * Fires before the load method's callback is called.
20096          * @param {Object} This DataProxy object.
20097          * @param {Object} o The data object.
20098          * @param {Object} arg The callback argument object passed to the load function.
20099          */
20100         load : true,
20101         /**
20102          * @event loadexception
20103          * Fires if an Exception occurs during data retrieval.
20104          * @param {Object} This DataProxy object.
20105          * @param {Object} o The data object.
20106          * @param {Object} arg The callback argument object passed to the load function.
20107          * @param {Object} e The Exception.
20108          */
20109         loadexception : true
20110     });
20111     Roo.data.DataProxy.superclass.constructor.call(this);
20112 };
20113
20114 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20115
20116     /**
20117      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20118      */
20119 /*
20120  * Based on:
20121  * Ext JS Library 1.1.1
20122  * Copyright(c) 2006-2007, Ext JS, LLC.
20123  *
20124  * Originally Released Under LGPL - original licence link has changed is not relivant.
20125  *
20126  * Fork - LGPL
20127  * <script type="text/javascript">
20128  */
20129 /**
20130  * @class Roo.data.MemoryProxy
20131  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20132  * to the Reader when its load method is called.
20133  * @constructor
20134  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20135  */
20136 Roo.data.MemoryProxy = function(data){
20137     if (data.data) {
20138         data = data.data;
20139     }
20140     Roo.data.MemoryProxy.superclass.constructor.call(this);
20141     this.data = data;
20142 };
20143
20144 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20145     /**
20146      * Load data from the requested source (in this case an in-memory
20147      * data object passed to the constructor), read the data object into
20148      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20149      * process that block using the passed callback.
20150      * @param {Object} params This parameter is not used by the MemoryProxy class.
20151      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20152      * object into a block of Roo.data.Records.
20153      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20154      * The function must be passed <ul>
20155      * <li>The Record block object</li>
20156      * <li>The "arg" argument from the load function</li>
20157      * <li>A boolean success indicator</li>
20158      * </ul>
20159      * @param {Object} scope The scope in which to call the callback
20160      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20161      */
20162     load : function(params, reader, callback, scope, arg){
20163         params = params || {};
20164         var result;
20165         try {
20166             result = reader.readRecords(this.data);
20167         }catch(e){
20168             this.fireEvent("loadexception", this, arg, null, e);
20169             callback.call(scope, null, arg, false);
20170             return;
20171         }
20172         callback.call(scope, result, arg, true);
20173     },
20174     
20175     // private
20176     update : function(params, records){
20177         
20178     }
20179 });/*
20180  * Based on:
20181  * Ext JS Library 1.1.1
20182  * Copyright(c) 2006-2007, Ext JS, LLC.
20183  *
20184  * Originally Released Under LGPL - original licence link has changed is not relivant.
20185  *
20186  * Fork - LGPL
20187  * <script type="text/javascript">
20188  */
20189 /**
20190  * @class Roo.data.HttpProxy
20191  * @extends Roo.data.DataProxy
20192  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20193  * configured to reference a certain URL.<br><br>
20194  * <p>
20195  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20196  * from which the running page was served.<br><br>
20197  * <p>
20198  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20199  * <p>
20200  * Be aware that to enable the browser to parse an XML document, the server must set
20201  * the Content-Type header in the HTTP response to "text/xml".
20202  * @constructor
20203  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20204  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20205  * will be used to make the request.
20206  */
20207 Roo.data.HttpProxy = function(conn){
20208     Roo.data.HttpProxy.superclass.constructor.call(this);
20209     // is conn a conn config or a real conn?
20210     this.conn = conn;
20211     this.useAjax = !conn || !conn.events;
20212   
20213 };
20214
20215 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20216     // thse are take from connection...
20217     
20218     /**
20219      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20220      */
20221     /**
20222      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20223      * extra parameters to each request made by this object. (defaults to undefined)
20224      */
20225     /**
20226      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20227      *  to each request made by this object. (defaults to undefined)
20228      */
20229     /**
20230      * @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)
20231      */
20232     /**
20233      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20234      */
20235      /**
20236      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20237      * @type Boolean
20238      */
20239   
20240
20241     /**
20242      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20243      * @type Boolean
20244      */
20245     /**
20246      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20247      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20248      * a finer-grained basis than the DataProxy events.
20249      */
20250     getConnection : function(){
20251         return this.useAjax ? Roo.Ajax : this.conn;
20252     },
20253
20254     /**
20255      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20256      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20257      * process that block using the passed callback.
20258      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20259      * for the request to the remote server.
20260      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20261      * object into a block of Roo.data.Records.
20262      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20263      * The function must be passed <ul>
20264      * <li>The Record block object</li>
20265      * <li>The "arg" argument from the load function</li>
20266      * <li>A boolean success indicator</li>
20267      * </ul>
20268      * @param {Object} scope The scope in which to call the callback
20269      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20270      */
20271     load : function(params, reader, callback, scope, arg){
20272         if(this.fireEvent("beforeload", this, params) !== false){
20273             var  o = {
20274                 params : params || {},
20275                 request: {
20276                     callback : callback,
20277                     scope : scope,
20278                     arg : arg
20279                 },
20280                 reader: reader,
20281                 callback : this.loadResponse,
20282                 scope: this
20283             };
20284             if(this.useAjax){
20285                 Roo.applyIf(o, this.conn);
20286                 if(this.activeRequest){
20287                     Roo.Ajax.abort(this.activeRequest);
20288                 }
20289                 this.activeRequest = Roo.Ajax.request(o);
20290             }else{
20291                 this.conn.request(o);
20292             }
20293         }else{
20294             callback.call(scope||this, null, arg, false);
20295         }
20296     },
20297
20298     // private
20299     loadResponse : function(o, success, response){
20300         delete this.activeRequest;
20301         if(!success){
20302             this.fireEvent("loadexception", this, o, response);
20303             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20304             return;
20305         }
20306         var result;
20307         try {
20308             result = o.reader.read(response);
20309         }catch(e){
20310             this.fireEvent("loadexception", this, o, response, e);
20311             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20312             return;
20313         }
20314         
20315         this.fireEvent("load", this, o, o.request.arg);
20316         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20317     },
20318
20319     // private
20320     update : function(dataSet){
20321
20322     },
20323
20324     // private
20325     updateResponse : function(dataSet){
20326
20327     }
20328 });/*
20329  * Based on:
20330  * Ext JS Library 1.1.1
20331  * Copyright(c) 2006-2007, Ext JS, LLC.
20332  *
20333  * Originally Released Under LGPL - original licence link has changed is not relivant.
20334  *
20335  * Fork - LGPL
20336  * <script type="text/javascript">
20337  */
20338
20339 /**
20340  * @class Roo.data.ScriptTagProxy
20341  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20342  * other than the originating domain of the running page.<br><br>
20343  * <p>
20344  * <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
20345  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20346  * <p>
20347  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20348  * source code that is used as the source inside a &lt;script> tag.<br><br>
20349  * <p>
20350  * In order for the browser to process the returned data, the server must wrap the data object
20351  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20352  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20353  * depending on whether the callback name was passed:
20354  * <p>
20355  * <pre><code>
20356 boolean scriptTag = false;
20357 String cb = request.getParameter("callback");
20358 if (cb != null) {
20359     scriptTag = true;
20360     response.setContentType("text/javascript");
20361 } else {
20362     response.setContentType("application/x-json");
20363 }
20364 Writer out = response.getWriter();
20365 if (scriptTag) {
20366     out.write(cb + "(");
20367 }
20368 out.print(dataBlock.toJsonString());
20369 if (scriptTag) {
20370     out.write(");");
20371 }
20372 </pre></code>
20373  *
20374  * @constructor
20375  * @param {Object} config A configuration object.
20376  */
20377 Roo.data.ScriptTagProxy = function(config){
20378     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20379     Roo.apply(this, config);
20380     this.head = document.getElementsByTagName("head")[0];
20381 };
20382
20383 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20384
20385 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20386     /**
20387      * @cfg {String} url The URL from which to request the data object.
20388      */
20389     /**
20390      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20391      */
20392     timeout : 30000,
20393     /**
20394      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20395      * the server the name of the callback function set up by the load call to process the returned data object.
20396      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20397      * javascript output which calls this named function passing the data object as its only parameter.
20398      */
20399     callbackParam : "callback",
20400     /**
20401      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20402      * name to the request.
20403      */
20404     nocache : true,
20405
20406     /**
20407      * Load data from the configured URL, read the data object into
20408      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20409      * process that block using the passed callback.
20410      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20411      * for the request to the remote server.
20412      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20413      * object into a block of Roo.data.Records.
20414      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20415      * The function must be passed <ul>
20416      * <li>The Record block object</li>
20417      * <li>The "arg" argument from the load function</li>
20418      * <li>A boolean success indicator</li>
20419      * </ul>
20420      * @param {Object} scope The scope in which to call the callback
20421      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20422      */
20423     load : function(params, reader, callback, scope, arg){
20424         if(this.fireEvent("beforeload", this, params) !== false){
20425
20426             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20427
20428             var url = this.url;
20429             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20430             if(this.nocache){
20431                 url += "&_dc=" + (new Date().getTime());
20432             }
20433             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20434             var trans = {
20435                 id : transId,
20436                 cb : "stcCallback"+transId,
20437                 scriptId : "stcScript"+transId,
20438                 params : params,
20439                 arg : arg,
20440                 url : url,
20441                 callback : callback,
20442                 scope : scope,
20443                 reader : reader
20444             };
20445             var conn = this;
20446
20447             window[trans.cb] = function(o){
20448                 conn.handleResponse(o, trans);
20449             };
20450
20451             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20452
20453             if(this.autoAbort !== false){
20454                 this.abort();
20455             }
20456
20457             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20458
20459             var script = document.createElement("script");
20460             script.setAttribute("src", url);
20461             script.setAttribute("type", "text/javascript");
20462             script.setAttribute("id", trans.scriptId);
20463             this.head.appendChild(script);
20464
20465             this.trans = trans;
20466         }else{
20467             callback.call(scope||this, null, arg, false);
20468         }
20469     },
20470
20471     // private
20472     isLoading : function(){
20473         return this.trans ? true : false;
20474     },
20475
20476     /**
20477      * Abort the current server request.
20478      */
20479     abort : function(){
20480         if(this.isLoading()){
20481             this.destroyTrans(this.trans);
20482         }
20483     },
20484
20485     // private
20486     destroyTrans : function(trans, isLoaded){
20487         this.head.removeChild(document.getElementById(trans.scriptId));
20488         clearTimeout(trans.timeoutId);
20489         if(isLoaded){
20490             window[trans.cb] = undefined;
20491             try{
20492                 delete window[trans.cb];
20493             }catch(e){}
20494         }else{
20495             // if hasn't been loaded, wait for load to remove it to prevent script error
20496             window[trans.cb] = function(){
20497                 window[trans.cb] = undefined;
20498                 try{
20499                     delete window[trans.cb];
20500                 }catch(e){}
20501             };
20502         }
20503     },
20504
20505     // private
20506     handleResponse : function(o, trans){
20507         this.trans = false;
20508         this.destroyTrans(trans, true);
20509         var result;
20510         try {
20511             result = trans.reader.readRecords(o);
20512         }catch(e){
20513             this.fireEvent("loadexception", this, o, trans.arg, e);
20514             trans.callback.call(trans.scope||window, null, trans.arg, false);
20515             return;
20516         }
20517         this.fireEvent("load", this, o, trans.arg);
20518         trans.callback.call(trans.scope||window, result, trans.arg, true);
20519     },
20520
20521     // private
20522     handleFailure : function(trans){
20523         this.trans = false;
20524         this.destroyTrans(trans, false);
20525         this.fireEvent("loadexception", this, null, trans.arg);
20526         trans.callback.call(trans.scope||window, null, trans.arg, false);
20527     }
20528 });/*
20529  * Based on:
20530  * Ext JS Library 1.1.1
20531  * Copyright(c) 2006-2007, Ext JS, LLC.
20532  *
20533  * Originally Released Under LGPL - original licence link has changed is not relivant.
20534  *
20535  * Fork - LGPL
20536  * <script type="text/javascript">
20537  */
20538
20539 /**
20540  * @class Roo.data.JsonReader
20541  * @extends Roo.data.DataReader
20542  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20543  * based on mappings in a provided Roo.data.Record constructor.
20544  * 
20545  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20546  * in the reply previously. 
20547  * 
20548  * <p>
20549  * Example code:
20550  * <pre><code>
20551 var RecordDef = Roo.data.Record.create([
20552     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20553     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20554 ]);
20555 var myReader = new Roo.data.JsonReader({
20556     totalProperty: "results",    // The property which contains the total dataset size (optional)
20557     root: "rows",                // The property which contains an Array of row objects
20558     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20559 }, RecordDef);
20560 </code></pre>
20561  * <p>
20562  * This would consume a JSON file like this:
20563  * <pre><code>
20564 { 'results': 2, 'rows': [
20565     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20566     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20567 }
20568 </code></pre>
20569  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20570  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20571  * paged from the remote server.
20572  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20573  * @cfg {String} root name of the property which contains the Array of row objects.
20574  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20575  * @constructor
20576  * Create a new JsonReader
20577  * @param {Object} meta Metadata configuration options
20578  * @param {Object} recordType Either an Array of field definition objects,
20579  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20580  */
20581 Roo.data.JsonReader = function(meta, recordType){
20582     
20583     meta = meta || {};
20584     // set some defaults:
20585     Roo.applyIf(meta, {
20586         totalProperty: 'total',
20587         successProperty : 'success',
20588         root : 'data',
20589         id : 'id'
20590     });
20591     
20592     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20593 };
20594 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20595     
20596     /**
20597      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20598      * Used by Store query builder to append _requestMeta to params.
20599      * 
20600      */
20601     metaFromRemote : false,
20602     /**
20603      * This method is only used by a DataProxy which has retrieved data from a remote server.
20604      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20605      * @return {Object} data A data block which is used by an Roo.data.Store object as
20606      * a cache of Roo.data.Records.
20607      */
20608     read : function(response){
20609         var json = response.responseText;
20610        
20611         var o = /* eval:var:o */ eval("("+json+")");
20612         if(!o) {
20613             throw {message: "JsonReader.read: Json object not found"};
20614         }
20615         
20616         if(o.metaData){
20617             
20618             delete this.ef;
20619             this.metaFromRemote = true;
20620             this.meta = o.metaData;
20621             this.recordType = Roo.data.Record.create(o.metaData.fields);
20622             this.onMetaChange(this.meta, this.recordType, o);
20623         }
20624         return this.readRecords(o);
20625     },
20626
20627     // private function a store will implement
20628     onMetaChange : function(meta, recordType, o){
20629
20630     },
20631
20632     /**
20633          * @ignore
20634          */
20635     simpleAccess: function(obj, subsc) {
20636         return obj[subsc];
20637     },
20638
20639         /**
20640          * @ignore
20641          */
20642     getJsonAccessor: function(){
20643         var re = /[\[\.]/;
20644         return function(expr) {
20645             try {
20646                 return(re.test(expr))
20647                     ? new Function("obj", "return obj." + expr)
20648                     : function(obj){
20649                         return obj[expr];
20650                     };
20651             } catch(e){}
20652             return Roo.emptyFn;
20653         };
20654     }(),
20655
20656     /**
20657      * Create a data block containing Roo.data.Records from an XML document.
20658      * @param {Object} o An object which contains an Array of row objects in the property specified
20659      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20660      * which contains the total size of the dataset.
20661      * @return {Object} data A data block which is used by an Roo.data.Store object as
20662      * a cache of Roo.data.Records.
20663      */
20664     readRecords : function(o){
20665         /**
20666          * After any data loads, the raw JSON data is available for further custom processing.
20667          * @type Object
20668          */
20669         this.jsonData = o;
20670         var s = this.meta, Record = this.recordType,
20671             f = Record.prototype.fields, fi = f.items, fl = f.length;
20672
20673 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20674         if (!this.ef) {
20675             if(s.totalProperty) {
20676                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20677                 }
20678                 if(s.successProperty) {
20679                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20680                 }
20681                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20682                 if (s.id) {
20683                         var g = this.getJsonAccessor(s.id);
20684                         this.getId = function(rec) {
20685                                 var r = g(rec);
20686                                 return (r === undefined || r === "") ? null : r;
20687                         };
20688                 } else {
20689                         this.getId = function(){return null;};
20690                 }
20691             this.ef = [];
20692             for(var jj = 0; jj < fl; jj++){
20693                 f = fi[jj];
20694                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20695                 this.ef[jj] = this.getJsonAccessor(map);
20696             }
20697         }
20698
20699         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20700         if(s.totalProperty){
20701             var vt = parseInt(this.getTotal(o), 10);
20702             if(!isNaN(vt)){
20703                 totalRecords = vt;
20704             }
20705         }
20706         if(s.successProperty){
20707             var vs = this.getSuccess(o);
20708             if(vs === false || vs === 'false'){
20709                 success = false;
20710             }
20711         }
20712         var records = [];
20713             for(var i = 0; i < c; i++){
20714                     var n = root[i];
20715                 var values = {};
20716                 var id = this.getId(n);
20717                 for(var j = 0; j < fl; j++){
20718                     f = fi[j];
20719                 var v = this.ef[j](n);
20720                 if (!f.convert) {
20721                     Roo.log('missing convert for ' + f.name);
20722                     Roo.log(f);
20723                     continue;
20724                 }
20725                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20726                 }
20727                 var record = new Record(values, id);
20728                 record.json = n;
20729                 records[i] = record;
20730             }
20731             return {
20732                 success : success,
20733                 records : records,
20734                 totalRecords : totalRecords
20735             };
20736     }
20737 });/*
20738  * Based on:
20739  * Ext JS Library 1.1.1
20740  * Copyright(c) 2006-2007, Ext JS, LLC.
20741  *
20742  * Originally Released Under LGPL - original licence link has changed is not relivant.
20743  *
20744  * Fork - LGPL
20745  * <script type="text/javascript">
20746  */
20747
20748 /**
20749  * @class Roo.data.XmlReader
20750  * @extends Roo.data.DataReader
20751  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20752  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20753  * <p>
20754  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20755  * header in the HTTP response must be set to "text/xml".</em>
20756  * <p>
20757  * Example code:
20758  * <pre><code>
20759 var RecordDef = Roo.data.Record.create([
20760    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20761    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20762 ]);
20763 var myReader = new Roo.data.XmlReader({
20764    totalRecords: "results", // The element which contains the total dataset size (optional)
20765    record: "row",           // The repeated element which contains row information
20766    id: "id"                 // The element within the row that provides an ID for the record (optional)
20767 }, RecordDef);
20768 </code></pre>
20769  * <p>
20770  * This would consume an XML file like this:
20771  * <pre><code>
20772 &lt;?xml?>
20773 &lt;dataset>
20774  &lt;results>2&lt;/results>
20775  &lt;row>
20776    &lt;id>1&lt;/id>
20777    &lt;name>Bill&lt;/name>
20778    &lt;occupation>Gardener&lt;/occupation>
20779  &lt;/row>
20780  &lt;row>
20781    &lt;id>2&lt;/id>
20782    &lt;name>Ben&lt;/name>
20783    &lt;occupation>Horticulturalist&lt;/occupation>
20784  &lt;/row>
20785 &lt;/dataset>
20786 </code></pre>
20787  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20788  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20789  * paged from the remote server.
20790  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20791  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20792  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20793  * a record identifier value.
20794  * @constructor
20795  * Create a new XmlReader
20796  * @param {Object} meta Metadata configuration options
20797  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20798  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20799  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20800  */
20801 Roo.data.XmlReader = function(meta, recordType){
20802     meta = meta || {};
20803     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20804 };
20805 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20806     /**
20807      * This method is only used by a DataProxy which has retrieved data from a remote server.
20808          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20809          * to contain a method called 'responseXML' that returns an XML document object.
20810      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20811      * a cache of Roo.data.Records.
20812      */
20813     read : function(response){
20814         var doc = response.responseXML;
20815         if(!doc) {
20816             throw {message: "XmlReader.read: XML Document not available"};
20817         }
20818         return this.readRecords(doc);
20819     },
20820
20821     /**
20822      * Create a data block containing Roo.data.Records from an XML document.
20823          * @param {Object} doc A parsed XML document.
20824      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20825      * a cache of Roo.data.Records.
20826      */
20827     readRecords : function(doc){
20828         /**
20829          * After any data loads/reads, the raw XML Document is available for further custom processing.
20830          * @type XMLDocument
20831          */
20832         this.xmlData = doc;
20833         var root = doc.documentElement || doc;
20834         var q = Roo.DomQuery;
20835         var recordType = this.recordType, fields = recordType.prototype.fields;
20836         var sid = this.meta.id;
20837         var totalRecords = 0, success = true;
20838         if(this.meta.totalRecords){
20839             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20840         }
20841         
20842         if(this.meta.success){
20843             var sv = q.selectValue(this.meta.success, root, true);
20844             success = sv !== false && sv !== 'false';
20845         }
20846         var records = [];
20847         var ns = q.select(this.meta.record, root);
20848         for(var i = 0, len = ns.length; i < len; i++) {
20849                 var n = ns[i];
20850                 var values = {};
20851                 var id = sid ? q.selectValue(sid, n) : undefined;
20852                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20853                     var f = fields.items[j];
20854                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20855                     v = f.convert(v);
20856                     values[f.name] = v;
20857                 }
20858                 var record = new recordType(values, id);
20859                 record.node = n;
20860                 records[records.length] = record;
20861             }
20862
20863             return {
20864                 success : success,
20865                 records : records,
20866                 totalRecords : totalRecords || records.length
20867             };
20868     }
20869 });/*
20870  * Based on:
20871  * Ext JS Library 1.1.1
20872  * Copyright(c) 2006-2007, Ext JS, LLC.
20873  *
20874  * Originally Released Under LGPL - original licence link has changed is not relivant.
20875  *
20876  * Fork - LGPL
20877  * <script type="text/javascript">
20878  */
20879
20880 /**
20881  * @class Roo.data.ArrayReader
20882  * @extends Roo.data.DataReader
20883  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20884  * Each element of that Array represents a row of data fields. The
20885  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20886  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20887  * <p>
20888  * Example code:.
20889  * <pre><code>
20890 var RecordDef = Roo.data.Record.create([
20891     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20892     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20893 ]);
20894 var myReader = new Roo.data.ArrayReader({
20895     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20896 }, RecordDef);
20897 </code></pre>
20898  * <p>
20899  * This would consume an Array like this:
20900  * <pre><code>
20901 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20902   </code></pre>
20903  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20904  * @constructor
20905  * Create a new JsonReader
20906  * @param {Object} meta Metadata configuration options.
20907  * @param {Object} recordType Either an Array of field definition objects
20908  * as specified to {@link Roo.data.Record#create},
20909  * or an {@link Roo.data.Record} object
20910  * created using {@link Roo.data.Record#create}.
20911  */
20912 Roo.data.ArrayReader = function(meta, recordType){
20913     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20914 };
20915
20916 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20917     /**
20918      * Create a data block containing Roo.data.Records from an XML document.
20919      * @param {Object} o An Array of row objects which represents the dataset.
20920      * @return {Object} data A data block which is used by an Roo.data.Store object as
20921      * a cache of Roo.data.Records.
20922      */
20923     readRecords : function(o){
20924         var sid = this.meta ? this.meta.id : null;
20925         var recordType = this.recordType, fields = recordType.prototype.fields;
20926         var records = [];
20927         var root = o;
20928             for(var i = 0; i < root.length; i++){
20929                     var n = root[i];
20930                 var values = {};
20931                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20932                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20933                 var f = fields.items[j];
20934                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20935                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20936                 v = f.convert(v);
20937                 values[f.name] = v;
20938             }
20939                 var record = new recordType(values, id);
20940                 record.json = n;
20941                 records[records.length] = record;
20942             }
20943             return {
20944                 records : records,
20945                 totalRecords : records.length
20946             };
20947     }
20948 });/*
20949  * Based on:
20950  * Ext JS Library 1.1.1
20951  * Copyright(c) 2006-2007, Ext JS, LLC.
20952  *
20953  * Originally Released Under LGPL - original licence link has changed is not relivant.
20954  *
20955  * Fork - LGPL
20956  * <script type="text/javascript">
20957  */
20958
20959
20960 /**
20961  * @class Roo.data.Tree
20962  * @extends Roo.util.Observable
20963  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20964  * in the tree have most standard DOM functionality.
20965  * @constructor
20966  * @param {Node} root (optional) The root node
20967  */
20968 Roo.data.Tree = function(root){
20969    this.nodeHash = {};
20970    /**
20971     * The root node for this tree
20972     * @type Node
20973     */
20974    this.root = null;
20975    if(root){
20976        this.setRootNode(root);
20977    }
20978    this.addEvents({
20979        /**
20980         * @event append
20981         * Fires when a new child node is appended to a node in this tree.
20982         * @param {Tree} tree The owner tree
20983         * @param {Node} parent The parent node
20984         * @param {Node} node The newly appended node
20985         * @param {Number} index The index of the newly appended node
20986         */
20987        "append" : true,
20988        /**
20989         * @event remove
20990         * Fires when a child node is removed from a node in this tree.
20991         * @param {Tree} tree The owner tree
20992         * @param {Node} parent The parent node
20993         * @param {Node} node The child node removed
20994         */
20995        "remove" : true,
20996        /**
20997         * @event move
20998         * Fires when a node is moved to a new location in the tree
20999         * @param {Tree} tree The owner tree
21000         * @param {Node} node The node moved
21001         * @param {Node} oldParent The old parent of this node
21002         * @param {Node} newParent The new parent of this node
21003         * @param {Number} index The index it was moved to
21004         */
21005        "move" : true,
21006        /**
21007         * @event insert
21008         * Fires when a new child node is inserted in a node in this tree.
21009         * @param {Tree} tree The owner tree
21010         * @param {Node} parent The parent node
21011         * @param {Node} node The child node inserted
21012         * @param {Node} refNode The child node the node was inserted before
21013         */
21014        "insert" : true,
21015        /**
21016         * @event beforeappend
21017         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21018         * @param {Tree} tree The owner tree
21019         * @param {Node} parent The parent node
21020         * @param {Node} node The child node to be appended
21021         */
21022        "beforeappend" : true,
21023        /**
21024         * @event beforeremove
21025         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21026         * @param {Tree} tree The owner tree
21027         * @param {Node} parent The parent node
21028         * @param {Node} node The child node to be removed
21029         */
21030        "beforeremove" : true,
21031        /**
21032         * @event beforemove
21033         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21034         * @param {Tree} tree The owner tree
21035         * @param {Node} node The node being moved
21036         * @param {Node} oldParent The parent of the node
21037         * @param {Node} newParent The new parent the node is moving to
21038         * @param {Number} index The index it is being moved to
21039         */
21040        "beforemove" : true,
21041        /**
21042         * @event beforeinsert
21043         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21044         * @param {Tree} tree The owner tree
21045         * @param {Node} parent The parent node
21046         * @param {Node} node The child node to be inserted
21047         * @param {Node} refNode The child node the node is being inserted before
21048         */
21049        "beforeinsert" : true
21050    });
21051
21052     Roo.data.Tree.superclass.constructor.call(this);
21053 };
21054
21055 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21056     pathSeparator: "/",
21057
21058     proxyNodeEvent : function(){
21059         return this.fireEvent.apply(this, arguments);
21060     },
21061
21062     /**
21063      * Returns the root node for this tree.
21064      * @return {Node}
21065      */
21066     getRootNode : function(){
21067         return this.root;
21068     },
21069
21070     /**
21071      * Sets the root node for this tree.
21072      * @param {Node} node
21073      * @return {Node}
21074      */
21075     setRootNode : function(node){
21076         this.root = node;
21077         node.ownerTree = this;
21078         node.isRoot = true;
21079         this.registerNode(node);
21080         return node;
21081     },
21082
21083     /**
21084      * Gets a node in this tree by its id.
21085      * @param {String} id
21086      * @return {Node}
21087      */
21088     getNodeById : function(id){
21089         return this.nodeHash[id];
21090     },
21091
21092     registerNode : function(node){
21093         this.nodeHash[node.id] = node;
21094     },
21095
21096     unregisterNode : function(node){
21097         delete this.nodeHash[node.id];
21098     },
21099
21100     toString : function(){
21101         return "[Tree"+(this.id?" "+this.id:"")+"]";
21102     }
21103 });
21104
21105 /**
21106  * @class Roo.data.Node
21107  * @extends Roo.util.Observable
21108  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21109  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21110  * @constructor
21111  * @param {Object} attributes The attributes/config for the node
21112  */
21113 Roo.data.Node = function(attributes){
21114     /**
21115      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21116      * @type {Object}
21117      */
21118     this.attributes = attributes || {};
21119     this.leaf = this.attributes.leaf;
21120     /**
21121      * The node id. @type String
21122      */
21123     this.id = this.attributes.id;
21124     if(!this.id){
21125         this.id = Roo.id(null, "ynode-");
21126         this.attributes.id = this.id;
21127     }
21128     /**
21129      * All child nodes of this node. @type Array
21130      */
21131     this.childNodes = [];
21132     if(!this.childNodes.indexOf){ // indexOf is a must
21133         this.childNodes.indexOf = function(o){
21134             for(var i = 0, len = this.length; i < len; i++){
21135                 if(this[i] == o) {
21136                     return i;
21137                 }
21138             }
21139             return -1;
21140         };
21141     }
21142     /**
21143      * The parent node for this node. @type Node
21144      */
21145     this.parentNode = null;
21146     /**
21147      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21148      */
21149     this.firstChild = null;
21150     /**
21151      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21152      */
21153     this.lastChild = null;
21154     /**
21155      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21156      */
21157     this.previousSibling = null;
21158     /**
21159      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21160      */
21161     this.nextSibling = null;
21162
21163     this.addEvents({
21164        /**
21165         * @event append
21166         * Fires when a new child node is appended
21167         * @param {Tree} tree The owner tree
21168         * @param {Node} this This node
21169         * @param {Node} node The newly appended node
21170         * @param {Number} index The index of the newly appended node
21171         */
21172        "append" : true,
21173        /**
21174         * @event remove
21175         * Fires when a child node is removed
21176         * @param {Tree} tree The owner tree
21177         * @param {Node} this This node
21178         * @param {Node} node The removed node
21179         */
21180        "remove" : true,
21181        /**
21182         * @event move
21183         * Fires when this node is moved to a new location in the tree
21184         * @param {Tree} tree The owner tree
21185         * @param {Node} this This node
21186         * @param {Node} oldParent The old parent of this node
21187         * @param {Node} newParent The new parent of this node
21188         * @param {Number} index The index it was moved to
21189         */
21190        "move" : true,
21191        /**
21192         * @event insert
21193         * Fires when a new child node is inserted.
21194         * @param {Tree} tree The owner tree
21195         * @param {Node} this This node
21196         * @param {Node} node The child node inserted
21197         * @param {Node} refNode The child node the node was inserted before
21198         */
21199        "insert" : true,
21200        /**
21201         * @event beforeappend
21202         * Fires before a new child is appended, return false to cancel the append.
21203         * @param {Tree} tree The owner tree
21204         * @param {Node} this This node
21205         * @param {Node} node The child node to be appended
21206         */
21207        "beforeappend" : true,
21208        /**
21209         * @event beforeremove
21210         * Fires before a child is removed, return false to cancel the remove.
21211         * @param {Tree} tree The owner tree
21212         * @param {Node} this This node
21213         * @param {Node} node The child node to be removed
21214         */
21215        "beforeremove" : true,
21216        /**
21217         * @event beforemove
21218         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21219         * @param {Tree} tree The owner tree
21220         * @param {Node} this This node
21221         * @param {Node} oldParent The parent of this node
21222         * @param {Node} newParent The new parent this node is moving to
21223         * @param {Number} index The index it is being moved to
21224         */
21225        "beforemove" : true,
21226        /**
21227         * @event beforeinsert
21228         * Fires before a new child is inserted, return false to cancel the insert.
21229         * @param {Tree} tree The owner tree
21230         * @param {Node} this This node
21231         * @param {Node} node The child node to be inserted
21232         * @param {Node} refNode The child node the node is being inserted before
21233         */
21234        "beforeinsert" : true
21235    });
21236     this.listeners = this.attributes.listeners;
21237     Roo.data.Node.superclass.constructor.call(this);
21238 };
21239
21240 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21241     fireEvent : function(evtName){
21242         // first do standard event for this node
21243         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21244             return false;
21245         }
21246         // then bubble it up to the tree if the event wasn't cancelled
21247         var ot = this.getOwnerTree();
21248         if(ot){
21249             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21250                 return false;
21251             }
21252         }
21253         return true;
21254     },
21255
21256     /**
21257      * Returns true if this node is a leaf
21258      * @return {Boolean}
21259      */
21260     isLeaf : function(){
21261         return this.leaf === true;
21262     },
21263
21264     // private
21265     setFirstChild : function(node){
21266         this.firstChild = node;
21267     },
21268
21269     //private
21270     setLastChild : function(node){
21271         this.lastChild = node;
21272     },
21273
21274
21275     /**
21276      * Returns true if this node is the last child of its parent
21277      * @return {Boolean}
21278      */
21279     isLast : function(){
21280        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21281     },
21282
21283     /**
21284      * Returns true if this node is the first child of its parent
21285      * @return {Boolean}
21286      */
21287     isFirst : function(){
21288        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21289     },
21290
21291     hasChildNodes : function(){
21292         return !this.isLeaf() && this.childNodes.length > 0;
21293     },
21294
21295     /**
21296      * Insert node(s) as the last child node of this node.
21297      * @param {Node/Array} node The node or Array of nodes to append
21298      * @return {Node} The appended node if single append, or null if an array was passed
21299      */
21300     appendChild : function(node){
21301         var multi = false;
21302         if(node instanceof Array){
21303             multi = node;
21304         }else if(arguments.length > 1){
21305             multi = arguments;
21306         }
21307         // if passed an array or multiple args do them one by one
21308         if(multi){
21309             for(var i = 0, len = multi.length; i < len; i++) {
21310                 this.appendChild(multi[i]);
21311             }
21312         }else{
21313             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21314                 return false;
21315             }
21316             var index = this.childNodes.length;
21317             var oldParent = node.parentNode;
21318             // it's a move, make sure we move it cleanly
21319             if(oldParent){
21320                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21321                     return false;
21322                 }
21323                 oldParent.removeChild(node);
21324             }
21325             index = this.childNodes.length;
21326             if(index == 0){
21327                 this.setFirstChild(node);
21328             }
21329             this.childNodes.push(node);
21330             node.parentNode = this;
21331             var ps = this.childNodes[index-1];
21332             if(ps){
21333                 node.previousSibling = ps;
21334                 ps.nextSibling = node;
21335             }else{
21336                 node.previousSibling = null;
21337             }
21338             node.nextSibling = null;
21339             this.setLastChild(node);
21340             node.setOwnerTree(this.getOwnerTree());
21341             this.fireEvent("append", this.ownerTree, this, node, index);
21342             if(oldParent){
21343                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21344             }
21345             return node;
21346         }
21347     },
21348
21349     /**
21350      * Removes a child node from this node.
21351      * @param {Node} node The node to remove
21352      * @return {Node} The removed node
21353      */
21354     removeChild : function(node){
21355         var index = this.childNodes.indexOf(node);
21356         if(index == -1){
21357             return false;
21358         }
21359         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21360             return false;
21361         }
21362
21363         // remove it from childNodes collection
21364         this.childNodes.splice(index, 1);
21365
21366         // update siblings
21367         if(node.previousSibling){
21368             node.previousSibling.nextSibling = node.nextSibling;
21369         }
21370         if(node.nextSibling){
21371             node.nextSibling.previousSibling = node.previousSibling;
21372         }
21373
21374         // update child refs
21375         if(this.firstChild == node){
21376             this.setFirstChild(node.nextSibling);
21377         }
21378         if(this.lastChild == node){
21379             this.setLastChild(node.previousSibling);
21380         }
21381
21382         node.setOwnerTree(null);
21383         // clear any references from the node
21384         node.parentNode = null;
21385         node.previousSibling = null;
21386         node.nextSibling = null;
21387         this.fireEvent("remove", this.ownerTree, this, node);
21388         return node;
21389     },
21390
21391     /**
21392      * Inserts the first node before the second node in this nodes childNodes collection.
21393      * @param {Node} node The node to insert
21394      * @param {Node} refNode The node to insert before (if null the node is appended)
21395      * @return {Node} The inserted node
21396      */
21397     insertBefore : function(node, refNode){
21398         if(!refNode){ // like standard Dom, refNode can be null for append
21399             return this.appendChild(node);
21400         }
21401         // nothing to do
21402         if(node == refNode){
21403             return false;
21404         }
21405
21406         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21407             return false;
21408         }
21409         var index = this.childNodes.indexOf(refNode);
21410         var oldParent = node.parentNode;
21411         var refIndex = index;
21412
21413         // when moving internally, indexes will change after remove
21414         if(oldParent == this && this.childNodes.indexOf(node) < index){
21415             refIndex--;
21416         }
21417
21418         // it's a move, make sure we move it cleanly
21419         if(oldParent){
21420             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21421                 return false;
21422             }
21423             oldParent.removeChild(node);
21424         }
21425         if(refIndex == 0){
21426             this.setFirstChild(node);
21427         }
21428         this.childNodes.splice(refIndex, 0, node);
21429         node.parentNode = this;
21430         var ps = this.childNodes[refIndex-1];
21431         if(ps){
21432             node.previousSibling = ps;
21433             ps.nextSibling = node;
21434         }else{
21435             node.previousSibling = null;
21436         }
21437         node.nextSibling = refNode;
21438         refNode.previousSibling = node;
21439         node.setOwnerTree(this.getOwnerTree());
21440         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21441         if(oldParent){
21442             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21443         }
21444         return node;
21445     },
21446
21447     /**
21448      * Returns the child node at the specified index.
21449      * @param {Number} index
21450      * @return {Node}
21451      */
21452     item : function(index){
21453         return this.childNodes[index];
21454     },
21455
21456     /**
21457      * Replaces one child node in this node with another.
21458      * @param {Node} newChild The replacement node
21459      * @param {Node} oldChild The node to replace
21460      * @return {Node} The replaced node
21461      */
21462     replaceChild : function(newChild, oldChild){
21463         this.insertBefore(newChild, oldChild);
21464         this.removeChild(oldChild);
21465         return oldChild;
21466     },
21467
21468     /**
21469      * Returns the index of a child node
21470      * @param {Node} node
21471      * @return {Number} The index of the node or -1 if it was not found
21472      */
21473     indexOf : function(child){
21474         return this.childNodes.indexOf(child);
21475     },
21476
21477     /**
21478      * Returns the tree this node is in.
21479      * @return {Tree}
21480      */
21481     getOwnerTree : function(){
21482         // if it doesn't have one, look for one
21483         if(!this.ownerTree){
21484             var p = this;
21485             while(p){
21486                 if(p.ownerTree){
21487                     this.ownerTree = p.ownerTree;
21488                     break;
21489                 }
21490                 p = p.parentNode;
21491             }
21492         }
21493         return this.ownerTree;
21494     },
21495
21496     /**
21497      * Returns depth of this node (the root node has a depth of 0)
21498      * @return {Number}
21499      */
21500     getDepth : function(){
21501         var depth = 0;
21502         var p = this;
21503         while(p.parentNode){
21504             ++depth;
21505             p = p.parentNode;
21506         }
21507         return depth;
21508     },
21509
21510     // private
21511     setOwnerTree : function(tree){
21512         // if it's move, we need to update everyone
21513         if(tree != this.ownerTree){
21514             if(this.ownerTree){
21515                 this.ownerTree.unregisterNode(this);
21516             }
21517             this.ownerTree = tree;
21518             var cs = this.childNodes;
21519             for(var i = 0, len = cs.length; i < len; i++) {
21520                 cs[i].setOwnerTree(tree);
21521             }
21522             if(tree){
21523                 tree.registerNode(this);
21524             }
21525         }
21526     },
21527
21528     /**
21529      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21530      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21531      * @return {String} The path
21532      */
21533     getPath : function(attr){
21534         attr = attr || "id";
21535         var p = this.parentNode;
21536         var b = [this.attributes[attr]];
21537         while(p){
21538             b.unshift(p.attributes[attr]);
21539             p = p.parentNode;
21540         }
21541         var sep = this.getOwnerTree().pathSeparator;
21542         return sep + b.join(sep);
21543     },
21544
21545     /**
21546      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21547      * function call will be the scope provided or the current node. The arguments to the function
21548      * will be the args provided or the current node. If the function returns false at any point,
21549      * the bubble is stopped.
21550      * @param {Function} fn The function to call
21551      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21552      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21553      */
21554     bubble : function(fn, scope, args){
21555         var p = this;
21556         while(p){
21557             if(fn.call(scope || p, args || p) === false){
21558                 break;
21559             }
21560             p = p.parentNode;
21561         }
21562     },
21563
21564     /**
21565      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21566      * function call will be the scope provided or the current node. The arguments to the function
21567      * will be the args provided or the current node. If the function returns false at any point,
21568      * the cascade is stopped on that branch.
21569      * @param {Function} fn The function to call
21570      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21571      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21572      */
21573     cascade : function(fn, scope, args){
21574         if(fn.call(scope || this, args || this) !== false){
21575             var cs = this.childNodes;
21576             for(var i = 0, len = cs.length; i < len; i++) {
21577                 cs[i].cascade(fn, scope, args);
21578             }
21579         }
21580     },
21581
21582     /**
21583      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21584      * function call will be the scope provided or the current node. The arguments to the function
21585      * will be the args provided or the current node. If the function returns false at any point,
21586      * the iteration stops.
21587      * @param {Function} fn The function to call
21588      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21589      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21590      */
21591     eachChild : function(fn, scope, args){
21592         var cs = this.childNodes;
21593         for(var i = 0, len = cs.length; i < len; i++) {
21594                 if(fn.call(scope || this, args || cs[i]) === false){
21595                     break;
21596                 }
21597         }
21598     },
21599
21600     /**
21601      * Finds the first child that has the attribute with the specified value.
21602      * @param {String} attribute The attribute name
21603      * @param {Mixed} value The value to search for
21604      * @return {Node} The found child or null if none was found
21605      */
21606     findChild : function(attribute, value){
21607         var cs = this.childNodes;
21608         for(var i = 0, len = cs.length; i < len; i++) {
21609                 if(cs[i].attributes[attribute] == value){
21610                     return cs[i];
21611                 }
21612         }
21613         return null;
21614     },
21615
21616     /**
21617      * Finds the first child by a custom function. The child matches if the function passed
21618      * returns true.
21619      * @param {Function} fn
21620      * @param {Object} scope (optional)
21621      * @return {Node} The found child or null if none was found
21622      */
21623     findChildBy : function(fn, scope){
21624         var cs = this.childNodes;
21625         for(var i = 0, len = cs.length; i < len; i++) {
21626                 if(fn.call(scope||cs[i], cs[i]) === true){
21627                     return cs[i];
21628                 }
21629         }
21630         return null;
21631     },
21632
21633     /**
21634      * Sorts this nodes children using the supplied sort function
21635      * @param {Function} fn
21636      * @param {Object} scope (optional)
21637      */
21638     sort : function(fn, scope){
21639         var cs = this.childNodes;
21640         var len = cs.length;
21641         if(len > 0){
21642             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21643             cs.sort(sortFn);
21644             for(var i = 0; i < len; i++){
21645                 var n = cs[i];
21646                 n.previousSibling = cs[i-1];
21647                 n.nextSibling = cs[i+1];
21648                 if(i == 0){
21649                     this.setFirstChild(n);
21650                 }
21651                 if(i == len-1){
21652                     this.setLastChild(n);
21653                 }
21654             }
21655         }
21656     },
21657
21658     /**
21659      * Returns true if this node is an ancestor (at any point) of the passed node.
21660      * @param {Node} node
21661      * @return {Boolean}
21662      */
21663     contains : function(node){
21664         return node.isAncestor(this);
21665     },
21666
21667     /**
21668      * Returns true if the passed node is an ancestor (at any point) of this node.
21669      * @param {Node} node
21670      * @return {Boolean}
21671      */
21672     isAncestor : function(node){
21673         var p = this.parentNode;
21674         while(p){
21675             if(p == node){
21676                 return true;
21677             }
21678             p = p.parentNode;
21679         }
21680         return false;
21681     },
21682
21683     toString : function(){
21684         return "[Node"+(this.id?" "+this.id:"")+"]";
21685     }
21686 });/*
21687  * Based on:
21688  * Ext JS Library 1.1.1
21689  * Copyright(c) 2006-2007, Ext JS, LLC.
21690  *
21691  * Originally Released Under LGPL - original licence link has changed is not relivant.
21692  *
21693  * Fork - LGPL
21694  * <script type="text/javascript">
21695  */
21696  
21697
21698 /**
21699  * @class Roo.ComponentMgr
21700  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21701  * @singleton
21702  */
21703 Roo.ComponentMgr = function(){
21704     var all = new Roo.util.MixedCollection();
21705
21706     return {
21707         /**
21708          * Registers a component.
21709          * @param {Roo.Component} c The component
21710          */
21711         register : function(c){
21712             all.add(c);
21713         },
21714
21715         /**
21716          * Unregisters a component.
21717          * @param {Roo.Component} c The component
21718          */
21719         unregister : function(c){
21720             all.remove(c);
21721         },
21722
21723         /**
21724          * Returns a component by id
21725          * @param {String} id The component id
21726          */
21727         get : function(id){
21728             return all.get(id);
21729         },
21730
21731         /**
21732          * Registers a function that will be called when a specified component is added to ComponentMgr
21733          * @param {String} id The component id
21734          * @param {Funtction} fn The callback function
21735          * @param {Object} scope The scope of the callback
21736          */
21737         onAvailable : function(id, fn, scope){
21738             all.on("add", function(index, o){
21739                 if(o.id == id){
21740                     fn.call(scope || o, o);
21741                     all.un("add", fn, scope);
21742                 }
21743             });
21744         }
21745     };
21746 }();/*
21747  * Based on:
21748  * Ext JS Library 1.1.1
21749  * Copyright(c) 2006-2007, Ext JS, LLC.
21750  *
21751  * Originally Released Under LGPL - original licence link has changed is not relivant.
21752  *
21753  * Fork - LGPL
21754  * <script type="text/javascript">
21755  */
21756  
21757 /**
21758  * @class Roo.Component
21759  * @extends Roo.util.Observable
21760  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21761  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21762  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21763  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21764  * All visual components (widgets) that require rendering into a layout should subclass Component.
21765  * @constructor
21766  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21767  * 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
21768  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21769  */
21770 Roo.Component = function(config){
21771     config = config || {};
21772     if(config.tagName || config.dom || typeof config == "string"){ // element object
21773         config = {el: config, id: config.id || config};
21774     }
21775     this.initialConfig = config;
21776
21777     Roo.apply(this, config);
21778     this.addEvents({
21779         /**
21780          * @event disable
21781          * Fires after the component is disabled.
21782              * @param {Roo.Component} this
21783              */
21784         disable : true,
21785         /**
21786          * @event enable
21787          * Fires after the component is enabled.
21788              * @param {Roo.Component} this
21789              */
21790         enable : true,
21791         /**
21792          * @event beforeshow
21793          * Fires before the component is shown.  Return false to stop the show.
21794              * @param {Roo.Component} this
21795              */
21796         beforeshow : true,
21797         /**
21798          * @event show
21799          * Fires after the component is shown.
21800              * @param {Roo.Component} this
21801              */
21802         show : true,
21803         /**
21804          * @event beforehide
21805          * Fires before the component is hidden. Return false to stop the hide.
21806              * @param {Roo.Component} this
21807              */
21808         beforehide : true,
21809         /**
21810          * @event hide
21811          * Fires after the component is hidden.
21812              * @param {Roo.Component} this
21813              */
21814         hide : true,
21815         /**
21816          * @event beforerender
21817          * Fires before the component is rendered. Return false to stop the render.
21818              * @param {Roo.Component} this
21819              */
21820         beforerender : true,
21821         /**
21822          * @event render
21823          * Fires after the component is rendered.
21824              * @param {Roo.Component} this
21825              */
21826         render : true,
21827         /**
21828          * @event beforedestroy
21829          * Fires before the component is destroyed. Return false to stop the destroy.
21830              * @param {Roo.Component} this
21831              */
21832         beforedestroy : true,
21833         /**
21834          * @event destroy
21835          * Fires after the component is destroyed.
21836              * @param {Roo.Component} this
21837              */
21838         destroy : true
21839     });
21840     if(!this.id){
21841         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21842     }
21843     Roo.ComponentMgr.register(this);
21844     Roo.Component.superclass.constructor.call(this);
21845     this.initComponent();
21846     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21847         this.render(this.renderTo);
21848         delete this.renderTo;
21849     }
21850 };
21851
21852 // private
21853 Roo.Component.AUTO_ID = 1000;
21854
21855 Roo.extend(Roo.Component, Roo.util.Observable, {
21856     /**
21857      * @property {Boolean} hidden
21858      * true if this component is hidden. Read-only.
21859      */
21860     hidden : false,
21861     /**
21862      * true if this component is disabled. Read-only.
21863      */
21864     disabled : false,
21865     /**
21866      * true if this component has been rendered. Read-only.
21867      */
21868     rendered : false,
21869     
21870     /** @cfg {String} disableClass
21871      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21872      */
21873     disabledClass : "x-item-disabled",
21874         /** @cfg {Boolean} allowDomMove
21875          * Whether the component can move the Dom node when rendering (defaults to true).
21876          */
21877     allowDomMove : true,
21878     /** @cfg {String} hideMode
21879      * How this component should hidden. Supported values are
21880      * "visibility" (css visibility), "offsets" (negative offset position) and
21881      * "display" (css display) - defaults to "display".
21882      */
21883     hideMode: 'display',
21884
21885     // private
21886     ctype : "Roo.Component",
21887
21888     /** @cfg {String} actionMode 
21889      * which property holds the element that used for  hide() / show() / disable() / enable()
21890      * default is 'el' 
21891      */
21892     actionMode : "el",
21893
21894     // private
21895     getActionEl : function(){
21896         return this[this.actionMode];
21897     },
21898
21899     initComponent : Roo.emptyFn,
21900     /**
21901      * If this is a lazy rendering component, render it to its container element.
21902      * @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.
21903      */
21904     render : function(container, position){
21905         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21906             if(!container && this.el){
21907                 this.el = Roo.get(this.el);
21908                 container = this.el.dom.parentNode;
21909                 this.allowDomMove = false;
21910             }
21911             this.container = Roo.get(container);
21912             this.rendered = true;
21913             if(position !== undefined){
21914                 if(typeof position == 'number'){
21915                     position = this.container.dom.childNodes[position];
21916                 }else{
21917                     position = Roo.getDom(position);
21918                 }
21919             }
21920             this.onRender(this.container, position || null);
21921             if(this.cls){
21922                 this.el.addClass(this.cls);
21923                 delete this.cls;
21924             }
21925             if(this.style){
21926                 this.el.applyStyles(this.style);
21927                 delete this.style;
21928             }
21929             this.fireEvent("render", this);
21930             this.afterRender(this.container);
21931             if(this.hidden){
21932                 this.hide();
21933             }
21934             if(this.disabled){
21935                 this.disable();
21936             }
21937         }
21938         return this;
21939     },
21940
21941     // private
21942     // default function is not really useful
21943     onRender : function(ct, position){
21944         if(this.el){
21945             this.el = Roo.get(this.el);
21946             if(this.allowDomMove !== false){
21947                 ct.dom.insertBefore(this.el.dom, position);
21948             }
21949         }
21950     },
21951
21952     // private
21953     getAutoCreate : function(){
21954         var cfg = typeof this.autoCreate == "object" ?
21955                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21956         if(this.id && !cfg.id){
21957             cfg.id = this.id;
21958         }
21959         return cfg;
21960     },
21961
21962     // private
21963     afterRender : Roo.emptyFn,
21964
21965     /**
21966      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21967      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21968      */
21969     destroy : function(){
21970         if(this.fireEvent("beforedestroy", this) !== false){
21971             this.purgeListeners();
21972             this.beforeDestroy();
21973             if(this.rendered){
21974                 this.el.removeAllListeners();
21975                 this.el.remove();
21976                 if(this.actionMode == "container"){
21977                     this.container.remove();
21978                 }
21979             }
21980             this.onDestroy();
21981             Roo.ComponentMgr.unregister(this);
21982             this.fireEvent("destroy", this);
21983         }
21984     },
21985
21986         // private
21987     beforeDestroy : function(){
21988
21989     },
21990
21991         // private
21992         onDestroy : function(){
21993
21994     },
21995
21996     /**
21997      * Returns the underlying {@link Roo.Element}.
21998      * @return {Roo.Element} The element
21999      */
22000     getEl : function(){
22001         return this.el;
22002     },
22003
22004     /**
22005      * Returns the id of this component.
22006      * @return {String}
22007      */
22008     getId : function(){
22009         return this.id;
22010     },
22011
22012     /**
22013      * Try to focus this component.
22014      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22015      * @return {Roo.Component} this
22016      */
22017     focus : function(selectText){
22018         if(this.rendered){
22019             this.el.focus();
22020             if(selectText === true){
22021                 this.el.dom.select();
22022             }
22023         }
22024         return this;
22025     },
22026
22027     // private
22028     blur : function(){
22029         if(this.rendered){
22030             this.el.blur();
22031         }
22032         return this;
22033     },
22034
22035     /**
22036      * Disable this component.
22037      * @return {Roo.Component} this
22038      */
22039     disable : function(){
22040         if(this.rendered){
22041             this.onDisable();
22042         }
22043         this.disabled = true;
22044         this.fireEvent("disable", this);
22045         return this;
22046     },
22047
22048         // private
22049     onDisable : function(){
22050         this.getActionEl().addClass(this.disabledClass);
22051         this.el.dom.disabled = true;
22052     },
22053
22054     /**
22055      * Enable this component.
22056      * @return {Roo.Component} this
22057      */
22058     enable : function(){
22059         if(this.rendered){
22060             this.onEnable();
22061         }
22062         this.disabled = false;
22063         this.fireEvent("enable", this);
22064         return this;
22065     },
22066
22067         // private
22068     onEnable : function(){
22069         this.getActionEl().removeClass(this.disabledClass);
22070         this.el.dom.disabled = false;
22071     },
22072
22073     /**
22074      * Convenience function for setting disabled/enabled by boolean.
22075      * @param {Boolean} disabled
22076      */
22077     setDisabled : function(disabled){
22078         this[disabled ? "disable" : "enable"]();
22079     },
22080
22081     /**
22082      * Show this component.
22083      * @return {Roo.Component} this
22084      */
22085     show: function(){
22086         if(this.fireEvent("beforeshow", this) !== false){
22087             this.hidden = false;
22088             if(this.rendered){
22089                 this.onShow();
22090             }
22091             this.fireEvent("show", this);
22092         }
22093         return this;
22094     },
22095
22096     // private
22097     onShow : function(){
22098         var ae = this.getActionEl();
22099         if(this.hideMode == 'visibility'){
22100             ae.dom.style.visibility = "visible";
22101         }else if(this.hideMode == 'offsets'){
22102             ae.removeClass('x-hidden');
22103         }else{
22104             ae.dom.style.display = "";
22105         }
22106     },
22107
22108     /**
22109      * Hide this component.
22110      * @return {Roo.Component} this
22111      */
22112     hide: function(){
22113         if(this.fireEvent("beforehide", this) !== false){
22114             this.hidden = true;
22115             if(this.rendered){
22116                 this.onHide();
22117             }
22118             this.fireEvent("hide", this);
22119         }
22120         return this;
22121     },
22122
22123     // private
22124     onHide : function(){
22125         var ae = this.getActionEl();
22126         if(this.hideMode == 'visibility'){
22127             ae.dom.style.visibility = "hidden";
22128         }else if(this.hideMode == 'offsets'){
22129             ae.addClass('x-hidden');
22130         }else{
22131             ae.dom.style.display = "none";
22132         }
22133     },
22134
22135     /**
22136      * Convenience function to hide or show this component by boolean.
22137      * @param {Boolean} visible True to show, false to hide
22138      * @return {Roo.Component} this
22139      */
22140     setVisible: function(visible){
22141         if(visible) {
22142             this.show();
22143         }else{
22144             this.hide();
22145         }
22146         return this;
22147     },
22148
22149     /**
22150      * Returns true if this component is visible.
22151      */
22152     isVisible : function(){
22153         return this.getActionEl().isVisible();
22154     },
22155
22156     cloneConfig : function(overrides){
22157         overrides = overrides || {};
22158         var id = overrides.id || Roo.id();
22159         var cfg = Roo.applyIf(overrides, this.initialConfig);
22160         cfg.id = id; // prevent dup id
22161         return new this.constructor(cfg);
22162     }
22163 });/*
22164  * Based on:
22165  * Ext JS Library 1.1.1
22166  * Copyright(c) 2006-2007, Ext JS, LLC.
22167  *
22168  * Originally Released Under LGPL - original licence link has changed is not relivant.
22169  *
22170  * Fork - LGPL
22171  * <script type="text/javascript">
22172  */
22173  (function(){ 
22174 /**
22175  * @class Roo.Layer
22176  * @extends Roo.Element
22177  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22178  * automatic maintaining of shadow/shim positions.
22179  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22180  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22181  * you can pass a string with a CSS class name. False turns off the shadow.
22182  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22183  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22184  * @cfg {String} cls CSS class to add to the element
22185  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22186  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22187  * @constructor
22188  * @param {Object} config An object with config options.
22189  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22190  */
22191
22192 Roo.Layer = function(config, existingEl){
22193     config = config || {};
22194     var dh = Roo.DomHelper;
22195     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22196     if(existingEl){
22197         this.dom = Roo.getDom(existingEl);
22198     }
22199     if(!this.dom){
22200         var o = config.dh || {tag: "div", cls: "x-layer"};
22201         this.dom = dh.append(pel, o);
22202     }
22203     if(config.cls){
22204         this.addClass(config.cls);
22205     }
22206     this.constrain = config.constrain !== false;
22207     this.visibilityMode = Roo.Element.VISIBILITY;
22208     if(config.id){
22209         this.id = this.dom.id = config.id;
22210     }else{
22211         this.id = Roo.id(this.dom);
22212     }
22213     this.zindex = config.zindex || this.getZIndex();
22214     this.position("absolute", this.zindex);
22215     if(config.shadow){
22216         this.shadowOffset = config.shadowOffset || 4;
22217         this.shadow = new Roo.Shadow({
22218             offset : this.shadowOffset,
22219             mode : config.shadow
22220         });
22221     }else{
22222         this.shadowOffset = 0;
22223     }
22224     this.useShim = config.shim !== false && Roo.useShims;
22225     this.useDisplay = config.useDisplay;
22226     this.hide();
22227 };
22228
22229 var supr = Roo.Element.prototype;
22230
22231 // shims are shared among layer to keep from having 100 iframes
22232 var shims = [];
22233
22234 Roo.extend(Roo.Layer, Roo.Element, {
22235
22236     getZIndex : function(){
22237         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22238     },
22239
22240     getShim : function(){
22241         if(!this.useShim){
22242             return null;
22243         }
22244         if(this.shim){
22245             return this.shim;
22246         }
22247         var shim = shims.shift();
22248         if(!shim){
22249             shim = this.createShim();
22250             shim.enableDisplayMode('block');
22251             shim.dom.style.display = 'none';
22252             shim.dom.style.visibility = 'visible';
22253         }
22254         var pn = this.dom.parentNode;
22255         if(shim.dom.parentNode != pn){
22256             pn.insertBefore(shim.dom, this.dom);
22257         }
22258         shim.setStyle('z-index', this.getZIndex()-2);
22259         this.shim = shim;
22260         return shim;
22261     },
22262
22263     hideShim : function(){
22264         if(this.shim){
22265             this.shim.setDisplayed(false);
22266             shims.push(this.shim);
22267             delete this.shim;
22268         }
22269     },
22270
22271     disableShadow : function(){
22272         if(this.shadow){
22273             this.shadowDisabled = true;
22274             this.shadow.hide();
22275             this.lastShadowOffset = this.shadowOffset;
22276             this.shadowOffset = 0;
22277         }
22278     },
22279
22280     enableShadow : function(show){
22281         if(this.shadow){
22282             this.shadowDisabled = false;
22283             this.shadowOffset = this.lastShadowOffset;
22284             delete this.lastShadowOffset;
22285             if(show){
22286                 this.sync(true);
22287             }
22288         }
22289     },
22290
22291     // private
22292     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22293     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22294     sync : function(doShow){
22295         var sw = this.shadow;
22296         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22297             var sh = this.getShim();
22298
22299             var w = this.getWidth(),
22300                 h = this.getHeight();
22301
22302             var l = this.getLeft(true),
22303                 t = this.getTop(true);
22304
22305             if(sw && !this.shadowDisabled){
22306                 if(doShow && !sw.isVisible()){
22307                     sw.show(this);
22308                 }else{
22309                     sw.realign(l, t, w, h);
22310                 }
22311                 if(sh){
22312                     if(doShow){
22313                        sh.show();
22314                     }
22315                     // fit the shim behind the shadow, so it is shimmed too
22316                     var a = sw.adjusts, s = sh.dom.style;
22317                     s.left = (Math.min(l, l+a.l))+"px";
22318                     s.top = (Math.min(t, t+a.t))+"px";
22319                     s.width = (w+a.w)+"px";
22320                     s.height = (h+a.h)+"px";
22321                 }
22322             }else if(sh){
22323                 if(doShow){
22324                    sh.show();
22325                 }
22326                 sh.setSize(w, h);
22327                 sh.setLeftTop(l, t);
22328             }
22329             
22330         }
22331     },
22332
22333     // private
22334     destroy : function(){
22335         this.hideShim();
22336         if(this.shadow){
22337             this.shadow.hide();
22338         }
22339         this.removeAllListeners();
22340         var pn = this.dom.parentNode;
22341         if(pn){
22342             pn.removeChild(this.dom);
22343         }
22344         Roo.Element.uncache(this.id);
22345     },
22346
22347     remove : function(){
22348         this.destroy();
22349     },
22350
22351     // private
22352     beginUpdate : function(){
22353         this.updating = true;
22354     },
22355
22356     // private
22357     endUpdate : function(){
22358         this.updating = false;
22359         this.sync(true);
22360     },
22361
22362     // private
22363     hideUnders : function(negOffset){
22364         if(this.shadow){
22365             this.shadow.hide();
22366         }
22367         this.hideShim();
22368     },
22369
22370     // private
22371     constrainXY : function(){
22372         if(this.constrain){
22373             var vw = Roo.lib.Dom.getViewWidth(),
22374                 vh = Roo.lib.Dom.getViewHeight();
22375             var s = Roo.get(document).getScroll();
22376
22377             var xy = this.getXY();
22378             var x = xy[0], y = xy[1];   
22379             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22380             // only move it if it needs it
22381             var moved = false;
22382             // first validate right/bottom
22383             if((x + w) > vw+s.left){
22384                 x = vw - w - this.shadowOffset;
22385                 moved = true;
22386             }
22387             if((y + h) > vh+s.top){
22388                 y = vh - h - this.shadowOffset;
22389                 moved = true;
22390             }
22391             // then make sure top/left isn't negative
22392             if(x < s.left){
22393                 x = s.left;
22394                 moved = true;
22395             }
22396             if(y < s.top){
22397                 y = s.top;
22398                 moved = true;
22399             }
22400             if(moved){
22401                 if(this.avoidY){
22402                     var ay = this.avoidY;
22403                     if(y <= ay && (y+h) >= ay){
22404                         y = ay-h-5;   
22405                     }
22406                 }
22407                 xy = [x, y];
22408                 this.storeXY(xy);
22409                 supr.setXY.call(this, xy);
22410                 this.sync();
22411             }
22412         }
22413     },
22414
22415     isVisible : function(){
22416         return this.visible;    
22417     },
22418
22419     // private
22420     showAction : function(){
22421         this.visible = true; // track visibility to prevent getStyle calls
22422         if(this.useDisplay === true){
22423             this.setDisplayed("");
22424         }else if(this.lastXY){
22425             supr.setXY.call(this, this.lastXY);
22426         }else if(this.lastLT){
22427             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22428         }
22429     },
22430
22431     // private
22432     hideAction : function(){
22433         this.visible = false;
22434         if(this.useDisplay === true){
22435             this.setDisplayed(false);
22436         }else{
22437             this.setLeftTop(-10000,-10000);
22438         }
22439     },
22440
22441     // overridden Element method
22442     setVisible : function(v, a, d, c, e){
22443         if(v){
22444             this.showAction();
22445         }
22446         if(a && v){
22447             var cb = function(){
22448                 this.sync(true);
22449                 if(c){
22450                     c();
22451                 }
22452             }.createDelegate(this);
22453             supr.setVisible.call(this, true, true, d, cb, e);
22454         }else{
22455             if(!v){
22456                 this.hideUnders(true);
22457             }
22458             var cb = c;
22459             if(a){
22460                 cb = function(){
22461                     this.hideAction();
22462                     if(c){
22463                         c();
22464                     }
22465                 }.createDelegate(this);
22466             }
22467             supr.setVisible.call(this, v, a, d, cb, e);
22468             if(v){
22469                 this.sync(true);
22470             }else if(!a){
22471                 this.hideAction();
22472             }
22473         }
22474     },
22475
22476     storeXY : function(xy){
22477         delete this.lastLT;
22478         this.lastXY = xy;
22479     },
22480
22481     storeLeftTop : function(left, top){
22482         delete this.lastXY;
22483         this.lastLT = [left, top];
22484     },
22485
22486     // private
22487     beforeFx : function(){
22488         this.beforeAction();
22489         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22490     },
22491
22492     // private
22493     afterFx : function(){
22494         Roo.Layer.superclass.afterFx.apply(this, arguments);
22495         this.sync(this.isVisible());
22496     },
22497
22498     // private
22499     beforeAction : function(){
22500         if(!this.updating && this.shadow){
22501             this.shadow.hide();
22502         }
22503     },
22504
22505     // overridden Element method
22506     setLeft : function(left){
22507         this.storeLeftTop(left, this.getTop(true));
22508         supr.setLeft.apply(this, arguments);
22509         this.sync();
22510     },
22511
22512     setTop : function(top){
22513         this.storeLeftTop(this.getLeft(true), top);
22514         supr.setTop.apply(this, arguments);
22515         this.sync();
22516     },
22517
22518     setLeftTop : function(left, top){
22519         this.storeLeftTop(left, top);
22520         supr.setLeftTop.apply(this, arguments);
22521         this.sync();
22522     },
22523
22524     setXY : function(xy, a, d, c, e){
22525         this.fixDisplay();
22526         this.beforeAction();
22527         this.storeXY(xy);
22528         var cb = this.createCB(c);
22529         supr.setXY.call(this, xy, a, d, cb, e);
22530         if(!a){
22531             cb();
22532         }
22533     },
22534
22535     // private
22536     createCB : function(c){
22537         var el = this;
22538         return function(){
22539             el.constrainXY();
22540             el.sync(true);
22541             if(c){
22542                 c();
22543             }
22544         };
22545     },
22546
22547     // overridden Element method
22548     setX : function(x, a, d, c, e){
22549         this.setXY([x, this.getY()], a, d, c, e);
22550     },
22551
22552     // overridden Element method
22553     setY : function(y, a, d, c, e){
22554         this.setXY([this.getX(), y], a, d, c, e);
22555     },
22556
22557     // overridden Element method
22558     setSize : function(w, h, a, d, c, e){
22559         this.beforeAction();
22560         var cb = this.createCB(c);
22561         supr.setSize.call(this, w, h, a, d, cb, e);
22562         if(!a){
22563             cb();
22564         }
22565     },
22566
22567     // overridden Element method
22568     setWidth : function(w, a, d, c, e){
22569         this.beforeAction();
22570         var cb = this.createCB(c);
22571         supr.setWidth.call(this, w, a, d, cb, e);
22572         if(!a){
22573             cb();
22574         }
22575     },
22576
22577     // overridden Element method
22578     setHeight : function(h, a, d, c, e){
22579         this.beforeAction();
22580         var cb = this.createCB(c);
22581         supr.setHeight.call(this, h, a, d, cb, e);
22582         if(!a){
22583             cb();
22584         }
22585     },
22586
22587     // overridden Element method
22588     setBounds : function(x, y, w, h, a, d, c, e){
22589         this.beforeAction();
22590         var cb = this.createCB(c);
22591         if(!a){
22592             this.storeXY([x, y]);
22593             supr.setXY.call(this, [x, y]);
22594             supr.setSize.call(this, w, h, a, d, cb, e);
22595             cb();
22596         }else{
22597             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22598         }
22599         return this;
22600     },
22601     
22602     /**
22603      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22604      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22605      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22606      * @param {Number} zindex The new z-index to set
22607      * @return {this} The Layer
22608      */
22609     setZIndex : function(zindex){
22610         this.zindex = zindex;
22611         this.setStyle("z-index", zindex + 2);
22612         if(this.shadow){
22613             this.shadow.setZIndex(zindex + 1);
22614         }
22615         if(this.shim){
22616             this.shim.setStyle("z-index", zindex);
22617         }
22618     }
22619 });
22620 })();/*
22621  * Based on:
22622  * Ext JS Library 1.1.1
22623  * Copyright(c) 2006-2007, Ext JS, LLC.
22624  *
22625  * Originally Released Under LGPL - original licence link has changed is not relivant.
22626  *
22627  * Fork - LGPL
22628  * <script type="text/javascript">
22629  */
22630
22631
22632 /**
22633  * @class Roo.Shadow
22634  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22635  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22636  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22637  * @constructor
22638  * Create a new Shadow
22639  * @param {Object} config The config object
22640  */
22641 Roo.Shadow = function(config){
22642     Roo.apply(this, config);
22643     if(typeof this.mode != "string"){
22644         this.mode = this.defaultMode;
22645     }
22646     var o = this.offset, a = {h: 0};
22647     var rad = Math.floor(this.offset/2);
22648     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22649         case "drop":
22650             a.w = 0;
22651             a.l = a.t = o;
22652             a.t -= 1;
22653             if(Roo.isIE){
22654                 a.l -= this.offset + rad;
22655                 a.t -= this.offset + rad;
22656                 a.w -= rad;
22657                 a.h -= rad;
22658                 a.t += 1;
22659             }
22660         break;
22661         case "sides":
22662             a.w = (o*2);
22663             a.l = -o;
22664             a.t = o-1;
22665             if(Roo.isIE){
22666                 a.l -= (this.offset - rad);
22667                 a.t -= this.offset + rad;
22668                 a.l += 1;
22669                 a.w -= (this.offset - rad)*2;
22670                 a.w -= rad + 1;
22671                 a.h -= 1;
22672             }
22673         break;
22674         case "frame":
22675             a.w = a.h = (o*2);
22676             a.l = a.t = -o;
22677             a.t += 1;
22678             a.h -= 2;
22679             if(Roo.isIE){
22680                 a.l -= (this.offset - rad);
22681                 a.t -= (this.offset - rad);
22682                 a.l += 1;
22683                 a.w -= (this.offset + rad + 1);
22684                 a.h -= (this.offset + rad);
22685                 a.h += 1;
22686             }
22687         break;
22688     };
22689
22690     this.adjusts = a;
22691 };
22692
22693 Roo.Shadow.prototype = {
22694     /**
22695      * @cfg {String} mode
22696      * The shadow display mode.  Supports the following options:<br />
22697      * sides: Shadow displays on both sides and bottom only<br />
22698      * frame: Shadow displays equally on all four sides<br />
22699      * drop: Traditional bottom-right drop shadow (default)
22700      */
22701     /**
22702      * @cfg {String} offset
22703      * The number of pixels to offset the shadow from the element (defaults to 4)
22704      */
22705     offset: 4,
22706
22707     // private
22708     defaultMode: "drop",
22709
22710     /**
22711      * Displays the shadow under the target element
22712      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22713      */
22714     show : function(target){
22715         target = Roo.get(target);
22716         if(!this.el){
22717             this.el = Roo.Shadow.Pool.pull();
22718             if(this.el.dom.nextSibling != target.dom){
22719                 this.el.insertBefore(target);
22720             }
22721         }
22722         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22723         if(Roo.isIE){
22724             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22725         }
22726         this.realign(
22727             target.getLeft(true),
22728             target.getTop(true),
22729             target.getWidth(),
22730             target.getHeight()
22731         );
22732         this.el.dom.style.display = "block";
22733     },
22734
22735     /**
22736      * Returns true if the shadow is visible, else false
22737      */
22738     isVisible : function(){
22739         return this.el ? true : false;  
22740     },
22741
22742     /**
22743      * Direct alignment when values are already available. Show must be called at least once before
22744      * calling this method to ensure it is initialized.
22745      * @param {Number} left The target element left position
22746      * @param {Number} top The target element top position
22747      * @param {Number} width The target element width
22748      * @param {Number} height The target element height
22749      */
22750     realign : function(l, t, w, h){
22751         if(!this.el){
22752             return;
22753         }
22754         var a = this.adjusts, d = this.el.dom, s = d.style;
22755         var iea = 0;
22756         s.left = (l+a.l)+"px";
22757         s.top = (t+a.t)+"px";
22758         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22759  
22760         if(s.width != sws || s.height != shs){
22761             s.width = sws;
22762             s.height = shs;
22763             if(!Roo.isIE){
22764                 var cn = d.childNodes;
22765                 var sww = Math.max(0, (sw-12))+"px";
22766                 cn[0].childNodes[1].style.width = sww;
22767                 cn[1].childNodes[1].style.width = sww;
22768                 cn[2].childNodes[1].style.width = sww;
22769                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22770             }
22771         }
22772     },
22773
22774     /**
22775      * Hides this shadow
22776      */
22777     hide : function(){
22778         if(this.el){
22779             this.el.dom.style.display = "none";
22780             Roo.Shadow.Pool.push(this.el);
22781             delete this.el;
22782         }
22783     },
22784
22785     /**
22786      * Adjust the z-index of this shadow
22787      * @param {Number} zindex The new z-index
22788      */
22789     setZIndex : function(z){
22790         this.zIndex = z;
22791         if(this.el){
22792             this.el.setStyle("z-index", z);
22793         }
22794     }
22795 };
22796
22797 // Private utility class that manages the internal Shadow cache
22798 Roo.Shadow.Pool = function(){
22799     var p = [];
22800     var markup = Roo.isIE ?
22801                  '<div class="x-ie-shadow"></div>' :
22802                  '<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>';
22803     return {
22804         pull : function(){
22805             var sh = p.shift();
22806             if(!sh){
22807                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22808                 sh.autoBoxAdjust = false;
22809             }
22810             return sh;
22811         },
22812
22813         push : function(sh){
22814             p.push(sh);
22815         }
22816     };
22817 }();/*
22818  * Based on:
22819  * Ext JS Library 1.1.1
22820  * Copyright(c) 2006-2007, Ext JS, LLC.
22821  *
22822  * Originally Released Under LGPL - original licence link has changed is not relivant.
22823  *
22824  * Fork - LGPL
22825  * <script type="text/javascript">
22826  */
22827
22828 /**
22829  * @class Roo.BoxComponent
22830  * @extends Roo.Component
22831  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22832  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22833  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22834  * layout containers.
22835  * @constructor
22836  * @param {Roo.Element/String/Object} config The configuration options.
22837  */
22838 Roo.BoxComponent = function(config){
22839     Roo.Component.call(this, config);
22840     this.addEvents({
22841         /**
22842          * @event resize
22843          * Fires after the component is resized.
22844              * @param {Roo.Component} this
22845              * @param {Number} adjWidth The box-adjusted width that was set
22846              * @param {Number} adjHeight The box-adjusted height that was set
22847              * @param {Number} rawWidth The width that was originally specified
22848              * @param {Number} rawHeight The height that was originally specified
22849              */
22850         resize : true,
22851         /**
22852          * @event move
22853          * Fires after the component is moved.
22854              * @param {Roo.Component} this
22855              * @param {Number} x The new x position
22856              * @param {Number} y The new y position
22857              */
22858         move : true
22859     });
22860 };
22861
22862 Roo.extend(Roo.BoxComponent, Roo.Component, {
22863     // private, set in afterRender to signify that the component has been rendered
22864     boxReady : false,
22865     // private, used to defer height settings to subclasses
22866     deferHeight: false,
22867     /** @cfg {Number} width
22868      * width (optional) size of component
22869      */
22870      /** @cfg {Number} height
22871      * height (optional) size of component
22872      */
22873      
22874     /**
22875      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22876      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22877      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22878      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22879      * @return {Roo.BoxComponent} this
22880      */
22881     setSize : function(w, h){
22882         // support for standard size objects
22883         if(typeof w == 'object'){
22884             h = w.height;
22885             w = w.width;
22886         }
22887         // not rendered
22888         if(!this.boxReady){
22889             this.width = w;
22890             this.height = h;
22891             return this;
22892         }
22893
22894         // prevent recalcs when not needed
22895         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22896             return this;
22897         }
22898         this.lastSize = {width: w, height: h};
22899
22900         var adj = this.adjustSize(w, h);
22901         var aw = adj.width, ah = adj.height;
22902         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22903             var rz = this.getResizeEl();
22904             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22905                 rz.setSize(aw, ah);
22906             }else if(!this.deferHeight && ah !== undefined){
22907                 rz.setHeight(ah);
22908             }else if(aw !== undefined){
22909                 rz.setWidth(aw);
22910             }
22911             this.onResize(aw, ah, w, h);
22912             this.fireEvent('resize', this, aw, ah, w, h);
22913         }
22914         return this;
22915     },
22916
22917     /**
22918      * Gets the current size of the component's underlying element.
22919      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22920      */
22921     getSize : function(){
22922         return this.el.getSize();
22923     },
22924
22925     /**
22926      * Gets the current XY position of the component's underlying element.
22927      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22928      * @return {Array} The XY position of the element (e.g., [100, 200])
22929      */
22930     getPosition : function(local){
22931         if(local === true){
22932             return [this.el.getLeft(true), this.el.getTop(true)];
22933         }
22934         return this.xy || this.el.getXY();
22935     },
22936
22937     /**
22938      * Gets the current box measurements of the component's underlying element.
22939      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22940      * @returns {Object} box An object in the format {x, y, width, height}
22941      */
22942     getBox : function(local){
22943         var s = this.el.getSize();
22944         if(local){
22945             s.x = this.el.getLeft(true);
22946             s.y = this.el.getTop(true);
22947         }else{
22948             var xy = this.xy || this.el.getXY();
22949             s.x = xy[0];
22950             s.y = xy[1];
22951         }
22952         return s;
22953     },
22954
22955     /**
22956      * Sets the current box measurements of the component's underlying element.
22957      * @param {Object} box An object in the format {x, y, width, height}
22958      * @returns {Roo.BoxComponent} this
22959      */
22960     updateBox : function(box){
22961         this.setSize(box.width, box.height);
22962         this.setPagePosition(box.x, box.y);
22963         return this;
22964     },
22965
22966     // protected
22967     getResizeEl : function(){
22968         return this.resizeEl || this.el;
22969     },
22970
22971     // protected
22972     getPositionEl : function(){
22973         return this.positionEl || this.el;
22974     },
22975
22976     /**
22977      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22978      * This method fires the move event.
22979      * @param {Number} left The new left
22980      * @param {Number} top The new top
22981      * @returns {Roo.BoxComponent} this
22982      */
22983     setPosition : function(x, y){
22984         this.x = x;
22985         this.y = y;
22986         if(!this.boxReady){
22987             return this;
22988         }
22989         var adj = this.adjustPosition(x, y);
22990         var ax = adj.x, ay = adj.y;
22991
22992         var el = this.getPositionEl();
22993         if(ax !== undefined || ay !== undefined){
22994             if(ax !== undefined && ay !== undefined){
22995                 el.setLeftTop(ax, ay);
22996             }else if(ax !== undefined){
22997                 el.setLeft(ax);
22998             }else if(ay !== undefined){
22999                 el.setTop(ay);
23000             }
23001             this.onPosition(ax, ay);
23002             this.fireEvent('move', this, ax, ay);
23003         }
23004         return this;
23005     },
23006
23007     /**
23008      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23009      * This method fires the move event.
23010      * @param {Number} x The new x position
23011      * @param {Number} y The new y position
23012      * @returns {Roo.BoxComponent} this
23013      */
23014     setPagePosition : function(x, y){
23015         this.pageX = x;
23016         this.pageY = y;
23017         if(!this.boxReady){
23018             return;
23019         }
23020         if(x === undefined || y === undefined){ // cannot translate undefined points
23021             return;
23022         }
23023         var p = this.el.translatePoints(x, y);
23024         this.setPosition(p.left, p.top);
23025         return this;
23026     },
23027
23028     // private
23029     onRender : function(ct, position){
23030         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23031         if(this.resizeEl){
23032             this.resizeEl = Roo.get(this.resizeEl);
23033         }
23034         if(this.positionEl){
23035             this.positionEl = Roo.get(this.positionEl);
23036         }
23037     },
23038
23039     // private
23040     afterRender : function(){
23041         Roo.BoxComponent.superclass.afterRender.call(this);
23042         this.boxReady = true;
23043         this.setSize(this.width, this.height);
23044         if(this.x || this.y){
23045             this.setPosition(this.x, this.y);
23046         }
23047         if(this.pageX || this.pageY){
23048             this.setPagePosition(this.pageX, this.pageY);
23049         }
23050     },
23051
23052     /**
23053      * Force the component's size to recalculate based on the underlying element's current height and width.
23054      * @returns {Roo.BoxComponent} this
23055      */
23056     syncSize : function(){
23057         delete this.lastSize;
23058         this.setSize(this.el.getWidth(), this.el.getHeight());
23059         return this;
23060     },
23061
23062     /**
23063      * Called after the component is resized, this method is empty by default but can be implemented by any
23064      * subclass that needs to perform custom logic after a resize occurs.
23065      * @param {Number} adjWidth The box-adjusted width that was set
23066      * @param {Number} adjHeight The box-adjusted height that was set
23067      * @param {Number} rawWidth The width that was originally specified
23068      * @param {Number} rawHeight The height that was originally specified
23069      */
23070     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23071
23072     },
23073
23074     /**
23075      * Called after the component is moved, this method is empty by default but can be implemented by any
23076      * subclass that needs to perform custom logic after a move occurs.
23077      * @param {Number} x The new x position
23078      * @param {Number} y The new y position
23079      */
23080     onPosition : function(x, y){
23081
23082     },
23083
23084     // private
23085     adjustSize : function(w, h){
23086         if(this.autoWidth){
23087             w = 'auto';
23088         }
23089         if(this.autoHeight){
23090             h = 'auto';
23091         }
23092         return {width : w, height: h};
23093     },
23094
23095     // private
23096     adjustPosition : function(x, y){
23097         return {x : x, y: y};
23098     }
23099 });/*
23100  * Based on:
23101  * Ext JS Library 1.1.1
23102  * Copyright(c) 2006-2007, Ext JS, LLC.
23103  *
23104  * Originally Released Under LGPL - original licence link has changed is not relivant.
23105  *
23106  * Fork - LGPL
23107  * <script type="text/javascript">
23108  */
23109
23110
23111 /**
23112  * @class Roo.SplitBar
23113  * @extends Roo.util.Observable
23114  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23115  * <br><br>
23116  * Usage:
23117  * <pre><code>
23118 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23119                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23120 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23121 split.minSize = 100;
23122 split.maxSize = 600;
23123 split.animate = true;
23124 split.on('moved', splitterMoved);
23125 </code></pre>
23126  * @constructor
23127  * Create a new SplitBar
23128  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23129  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23130  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23131  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23132                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23133                         position of the SplitBar).
23134  */
23135 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23136     
23137     /** @private */
23138     this.el = Roo.get(dragElement, true);
23139     this.el.dom.unselectable = "on";
23140     /** @private */
23141     this.resizingEl = Roo.get(resizingElement, true);
23142
23143     /**
23144      * @private
23145      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23146      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23147      * @type Number
23148      */
23149     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23150     
23151     /**
23152      * The minimum size of the resizing element. (Defaults to 0)
23153      * @type Number
23154      */
23155     this.minSize = 0;
23156     
23157     /**
23158      * The maximum size of the resizing element. (Defaults to 2000)
23159      * @type Number
23160      */
23161     this.maxSize = 2000;
23162     
23163     /**
23164      * Whether to animate the transition to the new size
23165      * @type Boolean
23166      */
23167     this.animate = false;
23168     
23169     /**
23170      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23171      * @type Boolean
23172      */
23173     this.useShim = false;
23174     
23175     /** @private */
23176     this.shim = null;
23177     
23178     if(!existingProxy){
23179         /** @private */
23180         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23181     }else{
23182         this.proxy = Roo.get(existingProxy).dom;
23183     }
23184     /** @private */
23185     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23186     
23187     /** @private */
23188     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23189     
23190     /** @private */
23191     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23192     
23193     /** @private */
23194     this.dragSpecs = {};
23195     
23196     /**
23197      * @private The adapter to use to positon and resize elements
23198      */
23199     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23200     this.adapter.init(this);
23201     
23202     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23203         /** @private */
23204         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23205         this.el.addClass("x-splitbar-h");
23206     }else{
23207         /** @private */
23208         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23209         this.el.addClass("x-splitbar-v");
23210     }
23211     
23212     this.addEvents({
23213         /**
23214          * @event resize
23215          * Fires when the splitter is moved (alias for {@link #event-moved})
23216          * @param {Roo.SplitBar} this
23217          * @param {Number} newSize the new width or height
23218          */
23219         "resize" : true,
23220         /**
23221          * @event moved
23222          * Fires when the splitter is moved
23223          * @param {Roo.SplitBar} this
23224          * @param {Number} newSize the new width or height
23225          */
23226         "moved" : true,
23227         /**
23228          * @event beforeresize
23229          * Fires before the splitter is dragged
23230          * @param {Roo.SplitBar} this
23231          */
23232         "beforeresize" : true,
23233
23234         "beforeapply" : true
23235     });
23236
23237     Roo.util.Observable.call(this);
23238 };
23239
23240 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23241     onStartProxyDrag : function(x, y){
23242         this.fireEvent("beforeresize", this);
23243         if(!this.overlay){
23244             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23245             o.unselectable();
23246             o.enableDisplayMode("block");
23247             // all splitbars share the same overlay
23248             Roo.SplitBar.prototype.overlay = o;
23249         }
23250         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23251         this.overlay.show();
23252         Roo.get(this.proxy).setDisplayed("block");
23253         var size = this.adapter.getElementSize(this);
23254         this.activeMinSize = this.getMinimumSize();;
23255         this.activeMaxSize = this.getMaximumSize();;
23256         var c1 = size - this.activeMinSize;
23257         var c2 = Math.max(this.activeMaxSize - size, 0);
23258         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23259             this.dd.resetConstraints();
23260             this.dd.setXConstraint(
23261                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23262                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23263             );
23264             this.dd.setYConstraint(0, 0);
23265         }else{
23266             this.dd.resetConstraints();
23267             this.dd.setXConstraint(0, 0);
23268             this.dd.setYConstraint(
23269                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23270                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23271             );
23272          }
23273         this.dragSpecs.startSize = size;
23274         this.dragSpecs.startPoint = [x, y];
23275         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23276     },
23277     
23278     /** 
23279      * @private Called after the drag operation by the DDProxy
23280      */
23281     onEndProxyDrag : function(e){
23282         Roo.get(this.proxy).setDisplayed(false);
23283         var endPoint = Roo.lib.Event.getXY(e);
23284         if(this.overlay){
23285             this.overlay.hide();
23286         }
23287         var newSize;
23288         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23289             newSize = this.dragSpecs.startSize + 
23290                 (this.placement == Roo.SplitBar.LEFT ?
23291                     endPoint[0] - this.dragSpecs.startPoint[0] :
23292                     this.dragSpecs.startPoint[0] - endPoint[0]
23293                 );
23294         }else{
23295             newSize = this.dragSpecs.startSize + 
23296                 (this.placement == Roo.SplitBar.TOP ?
23297                     endPoint[1] - this.dragSpecs.startPoint[1] :
23298                     this.dragSpecs.startPoint[1] - endPoint[1]
23299                 );
23300         }
23301         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23302         if(newSize != this.dragSpecs.startSize){
23303             if(this.fireEvent('beforeapply', this, newSize) !== false){
23304                 this.adapter.setElementSize(this, newSize);
23305                 this.fireEvent("moved", this, newSize);
23306                 this.fireEvent("resize", this, newSize);
23307             }
23308         }
23309     },
23310     
23311     /**
23312      * Get the adapter this SplitBar uses
23313      * @return The adapter object
23314      */
23315     getAdapter : function(){
23316         return this.adapter;
23317     },
23318     
23319     /**
23320      * Set the adapter this SplitBar uses
23321      * @param {Object} adapter A SplitBar adapter object
23322      */
23323     setAdapter : function(adapter){
23324         this.adapter = adapter;
23325         this.adapter.init(this);
23326     },
23327     
23328     /**
23329      * Gets the minimum size for the resizing element
23330      * @return {Number} The minimum size
23331      */
23332     getMinimumSize : function(){
23333         return this.minSize;
23334     },
23335     
23336     /**
23337      * Sets the minimum size for the resizing element
23338      * @param {Number} minSize The minimum size
23339      */
23340     setMinimumSize : function(minSize){
23341         this.minSize = minSize;
23342     },
23343     
23344     /**
23345      * Gets the maximum size for the resizing element
23346      * @return {Number} The maximum size
23347      */
23348     getMaximumSize : function(){
23349         return this.maxSize;
23350     },
23351     
23352     /**
23353      * Sets the maximum size for the resizing element
23354      * @param {Number} maxSize The maximum size
23355      */
23356     setMaximumSize : function(maxSize){
23357         this.maxSize = maxSize;
23358     },
23359     
23360     /**
23361      * Sets the initialize size for the resizing element
23362      * @param {Number} size The initial size
23363      */
23364     setCurrentSize : function(size){
23365         var oldAnimate = this.animate;
23366         this.animate = false;
23367         this.adapter.setElementSize(this, size);
23368         this.animate = oldAnimate;
23369     },
23370     
23371     /**
23372      * Destroy this splitbar. 
23373      * @param {Boolean} removeEl True to remove the element
23374      */
23375     destroy : function(removeEl){
23376         if(this.shim){
23377             this.shim.remove();
23378         }
23379         this.dd.unreg();
23380         this.proxy.parentNode.removeChild(this.proxy);
23381         if(removeEl){
23382             this.el.remove();
23383         }
23384     }
23385 });
23386
23387 /**
23388  * @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.
23389  */
23390 Roo.SplitBar.createProxy = function(dir){
23391     var proxy = new Roo.Element(document.createElement("div"));
23392     proxy.unselectable();
23393     var cls = 'x-splitbar-proxy';
23394     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23395     document.body.appendChild(proxy.dom);
23396     return proxy.dom;
23397 };
23398
23399 /** 
23400  * @class Roo.SplitBar.BasicLayoutAdapter
23401  * Default Adapter. It assumes the splitter and resizing element are not positioned
23402  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23403  */
23404 Roo.SplitBar.BasicLayoutAdapter = function(){
23405 };
23406
23407 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23408     // do nothing for now
23409     init : function(s){
23410     
23411     },
23412     /**
23413      * Called before drag operations to get the current size of the resizing element. 
23414      * @param {Roo.SplitBar} s The SplitBar using this adapter
23415      */
23416      getElementSize : function(s){
23417         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23418             return s.resizingEl.getWidth();
23419         }else{
23420             return s.resizingEl.getHeight();
23421         }
23422     },
23423     
23424     /**
23425      * Called after drag operations to set the size of the resizing element.
23426      * @param {Roo.SplitBar} s The SplitBar using this adapter
23427      * @param {Number} newSize The new size to set
23428      * @param {Function} onComplete A function to be invoked when resizing is complete
23429      */
23430     setElementSize : function(s, newSize, onComplete){
23431         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23432             if(!s.animate){
23433                 s.resizingEl.setWidth(newSize);
23434                 if(onComplete){
23435                     onComplete(s, newSize);
23436                 }
23437             }else{
23438                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23439             }
23440         }else{
23441             
23442             if(!s.animate){
23443                 s.resizingEl.setHeight(newSize);
23444                 if(onComplete){
23445                     onComplete(s, newSize);
23446                 }
23447             }else{
23448                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23449             }
23450         }
23451     }
23452 };
23453
23454 /** 
23455  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23456  * @extends Roo.SplitBar.BasicLayoutAdapter
23457  * Adapter that  moves the splitter element to align with the resized sizing element. 
23458  * Used with an absolute positioned SplitBar.
23459  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23460  * document.body, make sure you assign an id to the body element.
23461  */
23462 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23463     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23464     this.container = Roo.get(container);
23465 };
23466
23467 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23468     init : function(s){
23469         this.basic.init(s);
23470     },
23471     
23472     getElementSize : function(s){
23473         return this.basic.getElementSize(s);
23474     },
23475     
23476     setElementSize : function(s, newSize, onComplete){
23477         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23478     },
23479     
23480     moveSplitter : function(s){
23481         var yes = Roo.SplitBar;
23482         switch(s.placement){
23483             case yes.LEFT:
23484                 s.el.setX(s.resizingEl.getRight());
23485                 break;
23486             case yes.RIGHT:
23487                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23488                 break;
23489             case yes.TOP:
23490                 s.el.setY(s.resizingEl.getBottom());
23491                 break;
23492             case yes.BOTTOM:
23493                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23494                 break;
23495         }
23496     }
23497 };
23498
23499 /**
23500  * Orientation constant - Create a vertical SplitBar
23501  * @static
23502  * @type Number
23503  */
23504 Roo.SplitBar.VERTICAL = 1;
23505
23506 /**
23507  * Orientation constant - Create a horizontal SplitBar
23508  * @static
23509  * @type Number
23510  */
23511 Roo.SplitBar.HORIZONTAL = 2;
23512
23513 /**
23514  * Placement constant - The resizing element is to the left of the splitter element
23515  * @static
23516  * @type Number
23517  */
23518 Roo.SplitBar.LEFT = 1;
23519
23520 /**
23521  * Placement constant - The resizing element is to the right of the splitter element
23522  * @static
23523  * @type Number
23524  */
23525 Roo.SplitBar.RIGHT = 2;
23526
23527 /**
23528  * Placement constant - The resizing element is positioned above the splitter element
23529  * @static
23530  * @type Number
23531  */
23532 Roo.SplitBar.TOP = 3;
23533
23534 /**
23535  * Placement constant - The resizing element is positioned under splitter element
23536  * @static
23537  * @type Number
23538  */
23539 Roo.SplitBar.BOTTOM = 4;
23540 /*
23541  * Based on:
23542  * Ext JS Library 1.1.1
23543  * Copyright(c) 2006-2007, Ext JS, LLC.
23544  *
23545  * Originally Released Under LGPL - original licence link has changed is not relivant.
23546  *
23547  * Fork - LGPL
23548  * <script type="text/javascript">
23549  */
23550
23551 /**
23552  * @class Roo.View
23553  * @extends Roo.util.Observable
23554  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23555  * This class also supports single and multi selection modes. <br>
23556  * Create a data model bound view:
23557  <pre><code>
23558  var store = new Roo.data.Store(...);
23559
23560  var view = new Roo.View({
23561     el : "my-element",
23562     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23563  
23564     singleSelect: true,
23565     selectedClass: "ydataview-selected",
23566     store: store
23567  });
23568
23569  // listen for node click?
23570  view.on("click", function(vw, index, node, e){
23571  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23572  });
23573
23574  // load XML data
23575  dataModel.load("foobar.xml");
23576  </code></pre>
23577  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23578  * <br><br>
23579  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23580  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23581  * 
23582  * Note: old style constructor is still suported (container, template, config)
23583  * 
23584  * @constructor
23585  * Create a new View
23586  * @param {Object} config The config object
23587  * 
23588  */
23589 Roo.View = function(config, depreciated_tpl, depreciated_config){
23590     
23591     if (typeof(depreciated_tpl) == 'undefined') {
23592         // new way.. - universal constructor.
23593         Roo.apply(this, config);
23594         this.el  = Roo.get(this.el);
23595     } else {
23596         // old format..
23597         this.el  = Roo.get(config);
23598         this.tpl = depreciated_tpl;
23599         Roo.apply(this, depreciated_config);
23600     }
23601      
23602     
23603     if(typeof(this.tpl) == "string"){
23604         this.tpl = new Roo.Template(this.tpl);
23605     } else {
23606         // support xtype ctors..
23607         this.tpl = new Roo.factory(this.tpl, Roo);
23608     }
23609     
23610     
23611     this.tpl.compile();
23612    
23613
23614      
23615     /** @private */
23616     this.addEvents({
23617         /**
23618          * @event beforeclick
23619          * Fires before a click is processed. Returns false to cancel the default action.
23620          * @param {Roo.View} this
23621          * @param {Number} index The index of the target node
23622          * @param {HTMLElement} node The target node
23623          * @param {Roo.EventObject} e The raw event object
23624          */
23625             "beforeclick" : true,
23626         /**
23627          * @event click
23628          * Fires when a template node is clicked.
23629          * @param {Roo.View} this
23630          * @param {Number} index The index of the target node
23631          * @param {HTMLElement} node The target node
23632          * @param {Roo.EventObject} e The raw event object
23633          */
23634             "click" : true,
23635         /**
23636          * @event dblclick
23637          * Fires when a template node is double clicked.
23638          * @param {Roo.View} this
23639          * @param {Number} index The index of the target node
23640          * @param {HTMLElement} node The target node
23641          * @param {Roo.EventObject} e The raw event object
23642          */
23643             "dblclick" : true,
23644         /**
23645          * @event contextmenu
23646          * Fires when a template node is right clicked.
23647          * @param {Roo.View} this
23648          * @param {Number} index The index of the target node
23649          * @param {HTMLElement} node The target node
23650          * @param {Roo.EventObject} e The raw event object
23651          */
23652             "contextmenu" : true,
23653         /**
23654          * @event selectionchange
23655          * Fires when the selected nodes change.
23656          * @param {Roo.View} this
23657          * @param {Array} selections Array of the selected nodes
23658          */
23659             "selectionchange" : true,
23660     
23661         /**
23662          * @event beforeselect
23663          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23664          * @param {Roo.View} this
23665          * @param {HTMLElement} node The node to be selected
23666          * @param {Array} selections Array of currently selected nodes
23667          */
23668             "beforeselect" : true,
23669         /**
23670          * @event preparedata
23671          * Fires on every row to render, to allow you to change the data.
23672          * @param {Roo.View} this
23673          * @param {Object} data to be rendered (change this)
23674          * @param {Number} row being rendered
23675          * @param {Roo.data.Record} record being rendered.
23676          */
23677           "preparedata" : true
23678         });
23679
23680     this.el.on({
23681         "click": this.onClick,
23682         "dblclick": this.onDblClick,
23683         "contextmenu": this.onContextMenu,
23684         scope:this
23685     });
23686
23687     this.selections = [];
23688     this.nodes = [];
23689     this.cmp = new Roo.CompositeElementLite([]);
23690     if(this.store){
23691         this.store = Roo.factory(this.store, Roo.data);
23692         this.setStore(this.store, true);
23693     }
23694     Roo.View.superclass.constructor.call(this);
23695 };
23696
23697 Roo.extend(Roo.View, Roo.util.Observable, {
23698     
23699      /**
23700      * @cfg {Roo.data.Store} store Data store to load data from.
23701      */
23702     store : false,
23703     
23704     /**
23705      * @cfg {String|Roo.Element} el The container element.
23706      */
23707     el : '',
23708     
23709     /**
23710      * @cfg {String|Roo.Template} tpl The template used by this View 
23711      */
23712     tpl : false,
23713     
23714     /**
23715      * @cfg {String} selectedClass The css class to add to selected nodes
23716      */
23717     selectedClass : "x-view-selected",
23718      /**
23719      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23720      */
23721     emptyText : "",
23722     /**
23723      * @cfg {Boolean} multiSelect Allow multiple selection
23724      */
23725     
23726     multiSelect : false,
23727     /**
23728      * @cfg {Boolean} singleSelect Allow single selection
23729      */
23730     singleSelect:  false,
23731     
23732     /**
23733      * Returns the element this view is bound to.
23734      * @return {Roo.Element}
23735      */
23736     getEl : function(){
23737         return this.el;
23738     },
23739
23740     /**
23741      * Refreshes the view.
23742      */
23743     refresh : function(){
23744         var t = this.tpl;
23745         this.clearSelections();
23746         this.el.update("");
23747         var html = [];
23748         var records = this.store.getRange();
23749         if(records.length < 1){
23750             this.el.update(this.emptyText);
23751             return;
23752         }
23753         for(var i = 0, len = records.length; i < len; i++){
23754             var data = this.prepareData(records[i].data, i, records[i]);
23755             this.fireEvent("preparedata", this, data, i, records[i]);
23756             html[html.length] = t.apply(data);
23757         }
23758         this.el.update(html.join(""));
23759         this.nodes = this.el.dom.childNodes;
23760         this.updateIndexes(0);
23761     },
23762
23763     /**
23764      * Function to override to reformat the data that is sent to
23765      * the template for each node.
23766      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23767      * a JSON object for an UpdateManager bound view).
23768      */
23769     prepareData : function(data){
23770         return data;
23771     },
23772
23773     onUpdate : function(ds, record){
23774         this.clearSelections();
23775         var index = this.store.indexOf(record);
23776         var n = this.nodes[index];
23777         this.tpl.insertBefore(n, this.prepareData(record.data));
23778         n.parentNode.removeChild(n);
23779         this.updateIndexes(index, index);
23780     },
23781
23782     onAdd : function(ds, records, index){
23783         this.clearSelections();
23784         if(this.nodes.length == 0){
23785             this.refresh();
23786             return;
23787         }
23788         var n = this.nodes[index];
23789         for(var i = 0, len = records.length; i < len; i++){
23790             var d = this.prepareData(records[i].data);
23791             if(n){
23792                 this.tpl.insertBefore(n, d);
23793             }else{
23794                 this.tpl.append(this.el, d);
23795             }
23796         }
23797         this.updateIndexes(index);
23798     },
23799
23800     onRemove : function(ds, record, index){
23801         this.clearSelections();
23802         this.el.dom.removeChild(this.nodes[index]);
23803         this.updateIndexes(index);
23804     },
23805
23806     /**
23807      * Refresh an individual node.
23808      * @param {Number} index
23809      */
23810     refreshNode : function(index){
23811         this.onUpdate(this.store, this.store.getAt(index));
23812     },
23813
23814     updateIndexes : function(startIndex, endIndex){
23815         var ns = this.nodes;
23816         startIndex = startIndex || 0;
23817         endIndex = endIndex || ns.length - 1;
23818         for(var i = startIndex; i <= endIndex; i++){
23819             ns[i].nodeIndex = i;
23820         }
23821     },
23822
23823     /**
23824      * Changes the data store this view uses and refresh the view.
23825      * @param {Store} store
23826      */
23827     setStore : function(store, initial){
23828         if(!initial && this.store){
23829             this.store.un("datachanged", this.refresh);
23830             this.store.un("add", this.onAdd);
23831             this.store.un("remove", this.onRemove);
23832             this.store.un("update", this.onUpdate);
23833             this.store.un("clear", this.refresh);
23834         }
23835         if(store){
23836           
23837             store.on("datachanged", this.refresh, this);
23838             store.on("add", this.onAdd, this);
23839             store.on("remove", this.onRemove, this);
23840             store.on("update", this.onUpdate, this);
23841             store.on("clear", this.refresh, this);
23842         }
23843         
23844         if(store){
23845             this.refresh();
23846         }
23847     },
23848
23849     /**
23850      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23851      * @param {HTMLElement} node
23852      * @return {HTMLElement} The template node
23853      */
23854     findItemFromChild : function(node){
23855         var el = this.el.dom;
23856         if(!node || node.parentNode == el){
23857                     return node;
23858             }
23859             var p = node.parentNode;
23860             while(p && p != el){
23861             if(p.parentNode == el){
23862                 return p;
23863             }
23864             p = p.parentNode;
23865         }
23866             return null;
23867     },
23868
23869     /** @ignore */
23870     onClick : function(e){
23871         var item = this.findItemFromChild(e.getTarget());
23872         if(item){
23873             var index = this.indexOf(item);
23874             if(this.onItemClick(item, index, e) !== false){
23875                 this.fireEvent("click", this, index, item, e);
23876             }
23877         }else{
23878             this.clearSelections();
23879         }
23880     },
23881
23882     /** @ignore */
23883     onContextMenu : function(e){
23884         var item = this.findItemFromChild(e.getTarget());
23885         if(item){
23886             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23887         }
23888     },
23889
23890     /** @ignore */
23891     onDblClick : function(e){
23892         var item = this.findItemFromChild(e.getTarget());
23893         if(item){
23894             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23895         }
23896     },
23897
23898     onItemClick : function(item, index, e){
23899         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23900             return false;
23901         }
23902         if(this.multiSelect || this.singleSelect){
23903             if(this.multiSelect && e.shiftKey && this.lastSelection){
23904                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23905             }else{
23906                 this.select(item, this.multiSelect && e.ctrlKey);
23907                 this.lastSelection = item;
23908             }
23909             e.preventDefault();
23910         }
23911         return true;
23912     },
23913
23914     /**
23915      * Get the number of selected nodes.
23916      * @return {Number}
23917      */
23918     getSelectionCount : function(){
23919         return this.selections.length;
23920     },
23921
23922     /**
23923      * Get the currently selected nodes.
23924      * @return {Array} An array of HTMLElements
23925      */
23926     getSelectedNodes : function(){
23927         return this.selections;
23928     },
23929
23930     /**
23931      * Get the indexes of the selected nodes.
23932      * @return {Array}
23933      */
23934     getSelectedIndexes : function(){
23935         var indexes = [], s = this.selections;
23936         for(var i = 0, len = s.length; i < len; i++){
23937             indexes.push(s[i].nodeIndex);
23938         }
23939         return indexes;
23940     },
23941
23942     /**
23943      * Clear all selections
23944      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23945      */
23946     clearSelections : function(suppressEvent){
23947         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23948             this.cmp.elements = this.selections;
23949             this.cmp.removeClass(this.selectedClass);
23950             this.selections = [];
23951             if(!suppressEvent){
23952                 this.fireEvent("selectionchange", this, this.selections);
23953             }
23954         }
23955     },
23956
23957     /**
23958      * Returns true if the passed node is selected
23959      * @param {HTMLElement/Number} node The node or node index
23960      * @return {Boolean}
23961      */
23962     isSelected : function(node){
23963         var s = this.selections;
23964         if(s.length < 1){
23965             return false;
23966         }
23967         node = this.getNode(node);
23968         return s.indexOf(node) !== -1;
23969     },
23970
23971     /**
23972      * Selects nodes.
23973      * @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
23974      * @param {Boolean} keepExisting (optional) true to keep existing selections
23975      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23976      */
23977     select : function(nodeInfo, keepExisting, suppressEvent){
23978         if(nodeInfo instanceof Array){
23979             if(!keepExisting){
23980                 this.clearSelections(true);
23981             }
23982             for(var i = 0, len = nodeInfo.length; i < len; i++){
23983                 this.select(nodeInfo[i], true, true);
23984             }
23985         } else{
23986             var node = this.getNode(nodeInfo);
23987             if(node && !this.isSelected(node)){
23988                 if(!keepExisting){
23989                     this.clearSelections(true);
23990                 }
23991                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23992                     Roo.fly(node).addClass(this.selectedClass);
23993                     this.selections.push(node);
23994                     if(!suppressEvent){
23995                         this.fireEvent("selectionchange", this, this.selections);
23996                     }
23997                 }
23998             }
23999         }
24000     },
24001
24002     /**
24003      * Gets a template node.
24004      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24005      * @return {HTMLElement} The node or null if it wasn't found
24006      */
24007     getNode : function(nodeInfo){
24008         if(typeof nodeInfo == "string"){
24009             return document.getElementById(nodeInfo);
24010         }else if(typeof nodeInfo == "number"){
24011             return this.nodes[nodeInfo];
24012         }
24013         return nodeInfo;
24014     },
24015
24016     /**
24017      * Gets a range template nodes.
24018      * @param {Number} startIndex
24019      * @param {Number} endIndex
24020      * @return {Array} An array of nodes
24021      */
24022     getNodes : function(start, end){
24023         var ns = this.nodes;
24024         start = start || 0;
24025         end = typeof end == "undefined" ? ns.length - 1 : end;
24026         var nodes = [];
24027         if(start <= end){
24028             for(var i = start; i <= end; i++){
24029                 nodes.push(ns[i]);
24030             }
24031         } else{
24032             for(var i = start; i >= end; i--){
24033                 nodes.push(ns[i]);
24034             }
24035         }
24036         return nodes;
24037     },
24038
24039     /**
24040      * Finds the index of the passed node
24041      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24042      * @return {Number} The index of the node or -1
24043      */
24044     indexOf : function(node){
24045         node = this.getNode(node);
24046         if(typeof node.nodeIndex == "number"){
24047             return node.nodeIndex;
24048         }
24049         var ns = this.nodes;
24050         for(var i = 0, len = ns.length; i < len; i++){
24051             if(ns[i] == node){
24052                 return i;
24053             }
24054         }
24055         return -1;
24056     }
24057 });
24058 /*
24059  * Based on:
24060  * Ext JS Library 1.1.1
24061  * Copyright(c) 2006-2007, Ext JS, LLC.
24062  *
24063  * Originally Released Under LGPL - original licence link has changed is not relivant.
24064  *
24065  * Fork - LGPL
24066  * <script type="text/javascript">
24067  */
24068
24069 /**
24070  * @class Roo.JsonView
24071  * @extends Roo.View
24072  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24073 <pre><code>
24074 var view = new Roo.JsonView({
24075     container: "my-element",
24076     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24077     multiSelect: true, 
24078     jsonRoot: "data" 
24079 });
24080
24081 // listen for node click?
24082 view.on("click", function(vw, index, node, e){
24083     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24084 });
24085
24086 // direct load of JSON data
24087 view.load("foobar.php");
24088
24089 // Example from my blog list
24090 var tpl = new Roo.Template(
24091     '&lt;div class="entry"&gt;' +
24092     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24093     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24094     "&lt;/div&gt;&lt;hr /&gt;"
24095 );
24096
24097 var moreView = new Roo.JsonView({
24098     container :  "entry-list", 
24099     template : tpl,
24100     jsonRoot: "posts"
24101 });
24102 moreView.on("beforerender", this.sortEntries, this);
24103 moreView.load({
24104     url: "/blog/get-posts.php",
24105     params: "allposts=true",
24106     text: "Loading Blog Entries..."
24107 });
24108 </code></pre>
24109
24110 * Note: old code is supported with arguments : (container, template, config)
24111
24112
24113  * @constructor
24114  * Create a new JsonView
24115  * 
24116  * @param {Object} config The config object
24117  * 
24118  */
24119 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24120     
24121     
24122     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24123
24124     var um = this.el.getUpdateManager();
24125     um.setRenderer(this);
24126     um.on("update", this.onLoad, this);
24127     um.on("failure", this.onLoadException, this);
24128
24129     /**
24130      * @event beforerender
24131      * Fires before rendering of the downloaded JSON data.
24132      * @param {Roo.JsonView} this
24133      * @param {Object} data The JSON data loaded
24134      */
24135     /**
24136      * @event load
24137      * Fires when data is loaded.
24138      * @param {Roo.JsonView} this
24139      * @param {Object} data The JSON data loaded
24140      * @param {Object} response The raw Connect response object
24141      */
24142     /**
24143      * @event loadexception
24144      * Fires when loading fails.
24145      * @param {Roo.JsonView} this
24146      * @param {Object} response The raw Connect response object
24147      */
24148     this.addEvents({
24149         'beforerender' : true,
24150         'load' : true,
24151         'loadexception' : true
24152     });
24153 };
24154 Roo.extend(Roo.JsonView, Roo.View, {
24155     /**
24156      * @type {String} The root property in the loaded JSON object that contains the data
24157      */
24158     jsonRoot : "",
24159
24160     /**
24161      * Refreshes the view.
24162      */
24163     refresh : function(){
24164         this.clearSelections();
24165         this.el.update("");
24166         var html = [];
24167         var o = this.jsonData;
24168         if(o && o.length > 0){
24169             for(var i = 0, len = o.length; i < len; i++){
24170                 var data = this.prepareData(o[i], i, o);
24171                 html[html.length] = this.tpl.apply(data);
24172             }
24173         }else{
24174             html.push(this.emptyText);
24175         }
24176         this.el.update(html.join(""));
24177         this.nodes = this.el.dom.childNodes;
24178         this.updateIndexes(0);
24179     },
24180
24181     /**
24182      * 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.
24183      * @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:
24184      <pre><code>
24185      view.load({
24186          url: "your-url.php",
24187          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24188          callback: yourFunction,
24189          scope: yourObject, //(optional scope)
24190          discardUrl: false,
24191          nocache: false,
24192          text: "Loading...",
24193          timeout: 30,
24194          scripts: false
24195      });
24196      </code></pre>
24197      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24198      * 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.
24199      * @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}
24200      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24201      * @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.
24202      */
24203     load : function(){
24204         var um = this.el.getUpdateManager();
24205         um.update.apply(um, arguments);
24206     },
24207
24208     render : function(el, response){
24209         this.clearSelections();
24210         this.el.update("");
24211         var o;
24212         try{
24213             o = Roo.util.JSON.decode(response.responseText);
24214             if(this.jsonRoot){
24215                 
24216                 o = o[this.jsonRoot];
24217             }
24218         } catch(e){
24219         }
24220         /**
24221          * The current JSON data or null
24222          */
24223         this.jsonData = o;
24224         this.beforeRender();
24225         this.refresh();
24226     },
24227
24228 /**
24229  * Get the number of records in the current JSON dataset
24230  * @return {Number}
24231  */
24232     getCount : function(){
24233         return this.jsonData ? this.jsonData.length : 0;
24234     },
24235
24236 /**
24237  * Returns the JSON object for the specified node(s)
24238  * @param {HTMLElement/Array} node The node or an array of nodes
24239  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24240  * you get the JSON object for the node
24241  */
24242     getNodeData : function(node){
24243         if(node instanceof Array){
24244             var data = [];
24245             for(var i = 0, len = node.length; i < len; i++){
24246                 data.push(this.getNodeData(node[i]));
24247             }
24248             return data;
24249         }
24250         return this.jsonData[this.indexOf(node)] || null;
24251     },
24252
24253     beforeRender : function(){
24254         this.snapshot = this.jsonData;
24255         if(this.sortInfo){
24256             this.sort.apply(this, this.sortInfo);
24257         }
24258         this.fireEvent("beforerender", this, this.jsonData);
24259     },
24260
24261     onLoad : function(el, o){
24262         this.fireEvent("load", this, this.jsonData, o);
24263     },
24264
24265     onLoadException : function(el, o){
24266         this.fireEvent("loadexception", this, o);
24267     },
24268
24269 /**
24270  * Filter the data by a specific property.
24271  * @param {String} property A property on your JSON objects
24272  * @param {String/RegExp} value Either string that the property values
24273  * should start with, or a RegExp to test against the property
24274  */
24275     filter : function(property, value){
24276         if(this.jsonData){
24277             var data = [];
24278             var ss = this.snapshot;
24279             if(typeof value == "string"){
24280                 var vlen = value.length;
24281                 if(vlen == 0){
24282                     this.clearFilter();
24283                     return;
24284                 }
24285                 value = value.toLowerCase();
24286                 for(var i = 0, len = ss.length; i < len; i++){
24287                     var o = ss[i];
24288                     if(o[property].substr(0, vlen).toLowerCase() == value){
24289                         data.push(o);
24290                     }
24291                 }
24292             } else if(value.exec){ // regex?
24293                 for(var i = 0, len = ss.length; i < len; i++){
24294                     var o = ss[i];
24295                     if(value.test(o[property])){
24296                         data.push(o);
24297                     }
24298                 }
24299             } else{
24300                 return;
24301             }
24302             this.jsonData = data;
24303             this.refresh();
24304         }
24305     },
24306
24307 /**
24308  * Filter by a function. The passed function will be called with each
24309  * object in the current dataset. If the function returns true the value is kept,
24310  * otherwise it is filtered.
24311  * @param {Function} fn
24312  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24313  */
24314     filterBy : function(fn, scope){
24315         if(this.jsonData){
24316             var data = [];
24317             var ss = this.snapshot;
24318             for(var i = 0, len = ss.length; i < len; i++){
24319                 var o = ss[i];
24320                 if(fn.call(scope || this, o)){
24321                     data.push(o);
24322                 }
24323             }
24324             this.jsonData = data;
24325             this.refresh();
24326         }
24327     },
24328
24329 /**
24330  * Clears the current filter.
24331  */
24332     clearFilter : function(){
24333         if(this.snapshot && this.jsonData != this.snapshot){
24334             this.jsonData = this.snapshot;
24335             this.refresh();
24336         }
24337     },
24338
24339
24340 /**
24341  * Sorts the data for this view and refreshes it.
24342  * @param {String} property A property on your JSON objects to sort on
24343  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24344  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24345  */
24346     sort : function(property, dir, sortType){
24347         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24348         if(this.jsonData){
24349             var p = property;
24350             var dsc = dir && dir.toLowerCase() == "desc";
24351             var f = function(o1, o2){
24352                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24353                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24354                 ;
24355                 if(v1 < v2){
24356                     return dsc ? +1 : -1;
24357                 } else if(v1 > v2){
24358                     return dsc ? -1 : +1;
24359                 } else{
24360                     return 0;
24361                 }
24362             };
24363             this.jsonData.sort(f);
24364             this.refresh();
24365             if(this.jsonData != this.snapshot){
24366                 this.snapshot.sort(f);
24367             }
24368         }
24369     }
24370 });/*
24371  * Based on:
24372  * Ext JS Library 1.1.1
24373  * Copyright(c) 2006-2007, Ext JS, LLC.
24374  *
24375  * Originally Released Under LGPL - original licence link has changed is not relivant.
24376  *
24377  * Fork - LGPL
24378  * <script type="text/javascript">
24379  */
24380  
24381
24382 /**
24383  * @class Roo.ColorPalette
24384  * @extends Roo.Component
24385  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24386  * Here's an example of typical usage:
24387  * <pre><code>
24388 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24389 cp.render('my-div');
24390
24391 cp.on('select', function(palette, selColor){
24392     // do something with selColor
24393 });
24394 </code></pre>
24395  * @constructor
24396  * Create a new ColorPalette
24397  * @param {Object} config The config object
24398  */
24399 Roo.ColorPalette = function(config){
24400     Roo.ColorPalette.superclass.constructor.call(this, config);
24401     this.addEvents({
24402         /**
24403              * @event select
24404              * Fires when a color is selected
24405              * @param {ColorPalette} this
24406              * @param {String} color The 6-digit color hex code (without the # symbol)
24407              */
24408         select: true
24409     });
24410
24411     if(this.handler){
24412         this.on("select", this.handler, this.scope, true);
24413     }
24414 };
24415 Roo.extend(Roo.ColorPalette, Roo.Component, {
24416     /**
24417      * @cfg {String} itemCls
24418      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24419      */
24420     itemCls : "x-color-palette",
24421     /**
24422      * @cfg {String} value
24423      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24424      * the hex codes are case-sensitive.
24425      */
24426     value : null,
24427     clickEvent:'click',
24428     // private
24429     ctype: "Roo.ColorPalette",
24430
24431     /**
24432      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24433      */
24434     allowReselect : false,
24435
24436     /**
24437      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24438      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24439      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24440      * of colors with the width setting until the box is symmetrical.</p>
24441      * <p>You can override individual colors if needed:</p>
24442      * <pre><code>
24443 var cp = new Roo.ColorPalette();
24444 cp.colors[0] = "FF0000";  // change the first box to red
24445 </code></pre>
24446
24447 Or you can provide a custom array of your own for complete control:
24448 <pre><code>
24449 var cp = new Roo.ColorPalette();
24450 cp.colors = ["000000", "993300", "333300"];
24451 </code></pre>
24452      * @type Array
24453      */
24454     colors : [
24455         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24456         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24457         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24458         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24459         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24460     ],
24461
24462     // private
24463     onRender : function(container, position){
24464         var t = new Roo.MasterTemplate(
24465             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24466         );
24467         var c = this.colors;
24468         for(var i = 0, len = c.length; i < len; i++){
24469             t.add([c[i]]);
24470         }
24471         var el = document.createElement("div");
24472         el.className = this.itemCls;
24473         t.overwrite(el);
24474         container.dom.insertBefore(el, position);
24475         this.el = Roo.get(el);
24476         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24477         if(this.clickEvent != 'click'){
24478             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24479         }
24480     },
24481
24482     // private
24483     afterRender : function(){
24484         Roo.ColorPalette.superclass.afterRender.call(this);
24485         if(this.value){
24486             var s = this.value;
24487             this.value = null;
24488             this.select(s);
24489         }
24490     },
24491
24492     // private
24493     handleClick : function(e, t){
24494         e.preventDefault();
24495         if(!this.disabled){
24496             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24497             this.select(c.toUpperCase());
24498         }
24499     },
24500
24501     /**
24502      * Selects the specified color in the palette (fires the select event)
24503      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24504      */
24505     select : function(color){
24506         color = color.replace("#", "");
24507         if(color != this.value || this.allowReselect){
24508             var el = this.el;
24509             if(this.value){
24510                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24511             }
24512             el.child("a.color-"+color).addClass("x-color-palette-sel");
24513             this.value = color;
24514             this.fireEvent("select", this, color);
24515         }
24516     }
24517 });/*
24518  * Based on:
24519  * Ext JS Library 1.1.1
24520  * Copyright(c) 2006-2007, Ext JS, LLC.
24521  *
24522  * Originally Released Under LGPL - original licence link has changed is not relivant.
24523  *
24524  * Fork - LGPL
24525  * <script type="text/javascript">
24526  */
24527  
24528 /**
24529  * @class Roo.DatePicker
24530  * @extends Roo.Component
24531  * Simple date picker class.
24532  * @constructor
24533  * Create a new DatePicker
24534  * @param {Object} config The config object
24535  */
24536 Roo.DatePicker = function(config){
24537     Roo.DatePicker.superclass.constructor.call(this, config);
24538
24539     this.value = config && config.value ?
24540                  config.value.clearTime() : new Date().clearTime();
24541
24542     this.addEvents({
24543         /**
24544              * @event select
24545              * Fires when a date is selected
24546              * @param {DatePicker} this
24547              * @param {Date} date The selected date
24548              */
24549         select: true
24550     });
24551
24552     if(this.handler){
24553         this.on("select", this.handler,  this.scope || this);
24554     }
24555     // build the disabledDatesRE
24556     if(!this.disabledDatesRE && this.disabledDates){
24557         var dd = this.disabledDates;
24558         var re = "(?:";
24559         for(var i = 0; i < dd.length; i++){
24560             re += dd[i];
24561             if(i != dd.length-1) re += "|";
24562         }
24563         this.disabledDatesRE = new RegExp(re + ")");
24564     }
24565 };
24566
24567 Roo.extend(Roo.DatePicker, Roo.Component, {
24568     /**
24569      * @cfg {String} todayText
24570      * The text to display on the button that selects the current date (defaults to "Today")
24571      */
24572     todayText : "Today",
24573     /**
24574      * @cfg {String} okText
24575      * The text to display on the ok button
24576      */
24577     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24578     /**
24579      * @cfg {String} cancelText
24580      * The text to display on the cancel button
24581      */
24582     cancelText : "Cancel",
24583     /**
24584      * @cfg {String} todayTip
24585      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24586      */
24587     todayTip : "{0} (Spacebar)",
24588     /**
24589      * @cfg {Date} minDate
24590      * Minimum allowable date (JavaScript date object, defaults to null)
24591      */
24592     minDate : null,
24593     /**
24594      * @cfg {Date} maxDate
24595      * Maximum allowable date (JavaScript date object, defaults to null)
24596      */
24597     maxDate : null,
24598     /**
24599      * @cfg {String} minText
24600      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24601      */
24602     minText : "This date is before the minimum date",
24603     /**
24604      * @cfg {String} maxText
24605      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24606      */
24607     maxText : "This date is after the maximum date",
24608     /**
24609      * @cfg {String} format
24610      * The default date format string which can be overriden for localization support.  The format must be
24611      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24612      */
24613     format : "m/d/y",
24614     /**
24615      * @cfg {Array} disabledDays
24616      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24617      */
24618     disabledDays : null,
24619     /**
24620      * @cfg {String} disabledDaysText
24621      * The tooltip to display when the date falls on a disabled day (defaults to "")
24622      */
24623     disabledDaysText : "",
24624     /**
24625      * @cfg {RegExp} disabledDatesRE
24626      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24627      */
24628     disabledDatesRE : null,
24629     /**
24630      * @cfg {String} disabledDatesText
24631      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24632      */
24633     disabledDatesText : "",
24634     /**
24635      * @cfg {Boolean} constrainToViewport
24636      * True to constrain the date picker to the viewport (defaults to true)
24637      */
24638     constrainToViewport : true,
24639     /**
24640      * @cfg {Array} monthNames
24641      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24642      */
24643     monthNames : Date.monthNames,
24644     /**
24645      * @cfg {Array} dayNames
24646      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24647      */
24648     dayNames : Date.dayNames,
24649     /**
24650      * @cfg {String} nextText
24651      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24652      */
24653     nextText: 'Next Month (Control+Right)',
24654     /**
24655      * @cfg {String} prevText
24656      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24657      */
24658     prevText: 'Previous Month (Control+Left)',
24659     /**
24660      * @cfg {String} monthYearText
24661      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24662      */
24663     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24664     /**
24665      * @cfg {Number} startDay
24666      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24667      */
24668     startDay : 0,
24669     /**
24670      * @cfg {Bool} showClear
24671      * Show a clear button (usefull for date form elements that can be blank.)
24672      */
24673     
24674     showClear: false,
24675     
24676     /**
24677      * Sets the value of the date field
24678      * @param {Date} value The date to set
24679      */
24680     setValue : function(value){
24681         var old = this.value;
24682         this.value = value.clearTime(true);
24683         if(this.el){
24684             this.update(this.value);
24685         }
24686     },
24687
24688     /**
24689      * Gets the current selected value of the date field
24690      * @return {Date} The selected date
24691      */
24692     getValue : function(){
24693         return this.value;
24694     },
24695
24696     // private
24697     focus : function(){
24698         if(this.el){
24699             this.update(this.activeDate);
24700         }
24701     },
24702
24703     // private
24704     onRender : function(container, position){
24705         var m = [
24706              '<table cellspacing="0">',
24707                 '<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>',
24708                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24709         var dn = this.dayNames;
24710         for(var i = 0; i < 7; i++){
24711             var d = this.startDay+i;
24712             if(d > 6){
24713                 d = d-7;
24714             }
24715             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24716         }
24717         m[m.length] = "</tr></thead><tbody><tr>";
24718         for(var i = 0; i < 42; i++) {
24719             if(i % 7 == 0 && i != 0){
24720                 m[m.length] = "</tr><tr>";
24721             }
24722             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24723         }
24724         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24725             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24726
24727         var el = document.createElement("div");
24728         el.className = "x-date-picker";
24729         el.innerHTML = m.join("");
24730
24731         container.dom.insertBefore(el, position);
24732
24733         this.el = Roo.get(el);
24734         this.eventEl = Roo.get(el.firstChild);
24735
24736         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24737             handler: this.showPrevMonth,
24738             scope: this,
24739             preventDefault:true,
24740             stopDefault:true
24741         });
24742
24743         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24744             handler: this.showNextMonth,
24745             scope: this,
24746             preventDefault:true,
24747             stopDefault:true
24748         });
24749
24750         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24751
24752         this.monthPicker = this.el.down('div.x-date-mp');
24753         this.monthPicker.enableDisplayMode('block');
24754         
24755         var kn = new Roo.KeyNav(this.eventEl, {
24756             "left" : function(e){
24757                 e.ctrlKey ?
24758                     this.showPrevMonth() :
24759                     this.update(this.activeDate.add("d", -1));
24760             },
24761
24762             "right" : function(e){
24763                 e.ctrlKey ?
24764                     this.showNextMonth() :
24765                     this.update(this.activeDate.add("d", 1));
24766             },
24767
24768             "up" : function(e){
24769                 e.ctrlKey ?
24770                     this.showNextYear() :
24771                     this.update(this.activeDate.add("d", -7));
24772             },
24773
24774             "down" : function(e){
24775                 e.ctrlKey ?
24776                     this.showPrevYear() :
24777                     this.update(this.activeDate.add("d", 7));
24778             },
24779
24780             "pageUp" : function(e){
24781                 this.showNextMonth();
24782             },
24783
24784             "pageDown" : function(e){
24785                 this.showPrevMonth();
24786             },
24787
24788             "enter" : function(e){
24789                 e.stopPropagation();
24790                 return true;
24791             },
24792
24793             scope : this
24794         });
24795
24796         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24797
24798         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24799
24800         this.el.unselectable();
24801         
24802         this.cells = this.el.select("table.x-date-inner tbody td");
24803         this.textNodes = this.el.query("table.x-date-inner tbody span");
24804
24805         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24806             text: "&#160;",
24807             tooltip: this.monthYearText
24808         });
24809
24810         this.mbtn.on('click', this.showMonthPicker, this);
24811         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24812
24813
24814         var today = (new Date()).dateFormat(this.format);
24815         
24816         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24817         if (this.showClear) {
24818             baseTb.add( new Roo.Toolbar.Fill());
24819         }
24820         baseTb.add({
24821             text: String.format(this.todayText, today),
24822             tooltip: String.format(this.todayTip, today),
24823             handler: this.selectToday,
24824             scope: this
24825         });
24826         
24827         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24828             
24829         //});
24830         if (this.showClear) {
24831             
24832             baseTb.add( new Roo.Toolbar.Fill());
24833             baseTb.add({
24834                 text: '&#160;',
24835                 cls: 'x-btn-icon x-btn-clear',
24836                 handler: function() {
24837                     //this.value = '';
24838                     this.fireEvent("select", this, '');
24839                 },
24840                 scope: this
24841             });
24842         }
24843         
24844         
24845         if(Roo.isIE){
24846             this.el.repaint();
24847         }
24848         this.update(this.value);
24849     },
24850
24851     createMonthPicker : function(){
24852         if(!this.monthPicker.dom.firstChild){
24853             var buf = ['<table border="0" cellspacing="0">'];
24854             for(var i = 0; i < 6; i++){
24855                 buf.push(
24856                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24857                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24858                     i == 0 ?
24859                     '<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>' :
24860                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24861                 );
24862             }
24863             buf.push(
24864                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24865                     this.okText,
24866                     '</button><button type="button" class="x-date-mp-cancel">',
24867                     this.cancelText,
24868                     '</button></td></tr>',
24869                 '</table>'
24870             );
24871             this.monthPicker.update(buf.join(''));
24872             this.monthPicker.on('click', this.onMonthClick, this);
24873             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24874
24875             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24876             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24877
24878             this.mpMonths.each(function(m, a, i){
24879                 i += 1;
24880                 if((i%2) == 0){
24881                     m.dom.xmonth = 5 + Math.round(i * .5);
24882                 }else{
24883                     m.dom.xmonth = Math.round((i-1) * .5);
24884                 }
24885             });
24886         }
24887     },
24888
24889     showMonthPicker : function(){
24890         this.createMonthPicker();
24891         var size = this.el.getSize();
24892         this.monthPicker.setSize(size);
24893         this.monthPicker.child('table').setSize(size);
24894
24895         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24896         this.updateMPMonth(this.mpSelMonth);
24897         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24898         this.updateMPYear(this.mpSelYear);
24899
24900         this.monthPicker.slideIn('t', {duration:.2});
24901     },
24902
24903     updateMPYear : function(y){
24904         this.mpyear = y;
24905         var ys = this.mpYears.elements;
24906         for(var i = 1; i <= 10; i++){
24907             var td = ys[i-1], y2;
24908             if((i%2) == 0){
24909                 y2 = y + Math.round(i * .5);
24910                 td.firstChild.innerHTML = y2;
24911                 td.xyear = y2;
24912             }else{
24913                 y2 = y - (5-Math.round(i * .5));
24914                 td.firstChild.innerHTML = y2;
24915                 td.xyear = y2;
24916             }
24917             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24918         }
24919     },
24920
24921     updateMPMonth : function(sm){
24922         this.mpMonths.each(function(m, a, i){
24923             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24924         });
24925     },
24926
24927     selectMPMonth: function(m){
24928         
24929     },
24930
24931     onMonthClick : function(e, t){
24932         e.stopEvent();
24933         var el = new Roo.Element(t), pn;
24934         if(el.is('button.x-date-mp-cancel')){
24935             this.hideMonthPicker();
24936         }
24937         else if(el.is('button.x-date-mp-ok')){
24938             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24939             this.hideMonthPicker();
24940         }
24941         else if(pn = el.up('td.x-date-mp-month', 2)){
24942             this.mpMonths.removeClass('x-date-mp-sel');
24943             pn.addClass('x-date-mp-sel');
24944             this.mpSelMonth = pn.dom.xmonth;
24945         }
24946         else if(pn = el.up('td.x-date-mp-year', 2)){
24947             this.mpYears.removeClass('x-date-mp-sel');
24948             pn.addClass('x-date-mp-sel');
24949             this.mpSelYear = pn.dom.xyear;
24950         }
24951         else if(el.is('a.x-date-mp-prev')){
24952             this.updateMPYear(this.mpyear-10);
24953         }
24954         else if(el.is('a.x-date-mp-next')){
24955             this.updateMPYear(this.mpyear+10);
24956         }
24957     },
24958
24959     onMonthDblClick : function(e, t){
24960         e.stopEvent();
24961         var el = new Roo.Element(t), pn;
24962         if(pn = el.up('td.x-date-mp-month', 2)){
24963             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24964             this.hideMonthPicker();
24965         }
24966         else if(pn = el.up('td.x-date-mp-year', 2)){
24967             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24968             this.hideMonthPicker();
24969         }
24970     },
24971
24972     hideMonthPicker : function(disableAnim){
24973         if(this.monthPicker){
24974             if(disableAnim === true){
24975                 this.monthPicker.hide();
24976             }else{
24977                 this.monthPicker.slideOut('t', {duration:.2});
24978             }
24979         }
24980     },
24981
24982     // private
24983     showPrevMonth : function(e){
24984         this.update(this.activeDate.add("mo", -1));
24985     },
24986
24987     // private
24988     showNextMonth : function(e){
24989         this.update(this.activeDate.add("mo", 1));
24990     },
24991
24992     // private
24993     showPrevYear : function(){
24994         this.update(this.activeDate.add("y", -1));
24995     },
24996
24997     // private
24998     showNextYear : function(){
24999         this.update(this.activeDate.add("y", 1));
25000     },
25001
25002     // private
25003     handleMouseWheel : function(e){
25004         var delta = e.getWheelDelta();
25005         if(delta > 0){
25006             this.showPrevMonth();
25007             e.stopEvent();
25008         } else if(delta < 0){
25009             this.showNextMonth();
25010             e.stopEvent();
25011         }
25012     },
25013
25014     // private
25015     handleDateClick : function(e, t){
25016         e.stopEvent();
25017         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25018             this.setValue(new Date(t.dateValue));
25019             this.fireEvent("select", this, this.value);
25020         }
25021     },
25022
25023     // private
25024     selectToday : function(){
25025         this.setValue(new Date().clearTime());
25026         this.fireEvent("select", this, this.value);
25027     },
25028
25029     // private
25030     update : function(date){
25031         var vd = this.activeDate;
25032         this.activeDate = date;
25033         if(vd && this.el){
25034             var t = date.getTime();
25035             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25036                 this.cells.removeClass("x-date-selected");
25037                 this.cells.each(function(c){
25038                    if(c.dom.firstChild.dateValue == t){
25039                        c.addClass("x-date-selected");
25040                        setTimeout(function(){
25041                             try{c.dom.firstChild.focus();}catch(e){}
25042                        }, 50);
25043                        return false;
25044                    }
25045                 });
25046                 return;
25047             }
25048         }
25049         var days = date.getDaysInMonth();
25050         var firstOfMonth = date.getFirstDateOfMonth();
25051         var startingPos = firstOfMonth.getDay()-this.startDay;
25052
25053         if(startingPos <= this.startDay){
25054             startingPos += 7;
25055         }
25056
25057         var pm = date.add("mo", -1);
25058         var prevStart = pm.getDaysInMonth()-startingPos;
25059
25060         var cells = this.cells.elements;
25061         var textEls = this.textNodes;
25062         days += startingPos;
25063
25064         // convert everything to numbers so it's fast
25065         var day = 86400000;
25066         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25067         var today = new Date().clearTime().getTime();
25068         var sel = date.clearTime().getTime();
25069         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25070         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25071         var ddMatch = this.disabledDatesRE;
25072         var ddText = this.disabledDatesText;
25073         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25074         var ddaysText = this.disabledDaysText;
25075         var format = this.format;
25076
25077         var setCellClass = function(cal, cell){
25078             cell.title = "";
25079             var t = d.getTime();
25080             cell.firstChild.dateValue = t;
25081             if(t == today){
25082                 cell.className += " x-date-today";
25083                 cell.title = cal.todayText;
25084             }
25085             if(t == sel){
25086                 cell.className += " x-date-selected";
25087                 setTimeout(function(){
25088                     try{cell.firstChild.focus();}catch(e){}
25089                 }, 50);
25090             }
25091             // disabling
25092             if(t < min) {
25093                 cell.className = " x-date-disabled";
25094                 cell.title = cal.minText;
25095                 return;
25096             }
25097             if(t > max) {
25098                 cell.className = " x-date-disabled";
25099                 cell.title = cal.maxText;
25100                 return;
25101             }
25102             if(ddays){
25103                 if(ddays.indexOf(d.getDay()) != -1){
25104                     cell.title = ddaysText;
25105                     cell.className = " x-date-disabled";
25106                 }
25107             }
25108             if(ddMatch && format){
25109                 var fvalue = d.dateFormat(format);
25110                 if(ddMatch.test(fvalue)){
25111                     cell.title = ddText.replace("%0", fvalue);
25112                     cell.className = " x-date-disabled";
25113                 }
25114             }
25115         };
25116
25117         var i = 0;
25118         for(; i < startingPos; i++) {
25119             textEls[i].innerHTML = (++prevStart);
25120             d.setDate(d.getDate()+1);
25121             cells[i].className = "x-date-prevday";
25122             setCellClass(this, cells[i]);
25123         }
25124         for(; i < days; i++){
25125             intDay = i - startingPos + 1;
25126             textEls[i].innerHTML = (intDay);
25127             d.setDate(d.getDate()+1);
25128             cells[i].className = "x-date-active";
25129             setCellClass(this, cells[i]);
25130         }
25131         var extraDays = 0;
25132         for(; i < 42; i++) {
25133              textEls[i].innerHTML = (++extraDays);
25134              d.setDate(d.getDate()+1);
25135              cells[i].className = "x-date-nextday";
25136              setCellClass(this, cells[i]);
25137         }
25138
25139         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25140
25141         if(!this.internalRender){
25142             var main = this.el.dom.firstChild;
25143             var w = main.offsetWidth;
25144             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25145             Roo.fly(main).setWidth(w);
25146             this.internalRender = true;
25147             // opera does not respect the auto grow header center column
25148             // then, after it gets a width opera refuses to recalculate
25149             // without a second pass
25150             if(Roo.isOpera && !this.secondPass){
25151                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25152                 this.secondPass = true;
25153                 this.update.defer(10, this, [date]);
25154             }
25155         }
25156     }
25157 });        /*
25158  * Based on:
25159  * Ext JS Library 1.1.1
25160  * Copyright(c) 2006-2007, Ext JS, LLC.
25161  *
25162  * Originally Released Under LGPL - original licence link has changed is not relivant.
25163  *
25164  * Fork - LGPL
25165  * <script type="text/javascript">
25166  */
25167 /**
25168  * @class Roo.TabPanel
25169  * @extends Roo.util.Observable
25170  * A lightweight tab container.
25171  * <br><br>
25172  * Usage:
25173  * <pre><code>
25174 // basic tabs 1, built from existing content
25175 var tabs = new Roo.TabPanel("tabs1");
25176 tabs.addTab("script", "View Script");
25177 tabs.addTab("markup", "View Markup");
25178 tabs.activate("script");
25179
25180 // more advanced tabs, built from javascript
25181 var jtabs = new Roo.TabPanel("jtabs");
25182 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25183
25184 // set up the UpdateManager
25185 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25186 var updater = tab2.getUpdateManager();
25187 updater.setDefaultUrl("ajax1.htm");
25188 tab2.on('activate', updater.refresh, updater, true);
25189
25190 // Use setUrl for Ajax loading
25191 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25192 tab3.setUrl("ajax2.htm", null, true);
25193
25194 // Disabled tab
25195 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25196 tab4.disable();
25197
25198 jtabs.activate("jtabs-1");
25199  * </code></pre>
25200  * @constructor
25201  * Create a new TabPanel.
25202  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25203  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25204  */
25205 Roo.TabPanel = function(container, config){
25206     /**
25207     * The container element for this TabPanel.
25208     * @type Roo.Element
25209     */
25210     this.el = Roo.get(container, true);
25211     if(config){
25212         if(typeof config == "boolean"){
25213             this.tabPosition = config ? "bottom" : "top";
25214         }else{
25215             Roo.apply(this, config);
25216         }
25217     }
25218     if(this.tabPosition == "bottom"){
25219         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25220         this.el.addClass("x-tabs-bottom");
25221     }
25222     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25223     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25224     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25225     if(Roo.isIE){
25226         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25227     }
25228     if(this.tabPosition != "bottom"){
25229         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25230          * @type Roo.Element
25231          */
25232         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25233         this.el.addClass("x-tabs-top");
25234     }
25235     this.items = [];
25236
25237     this.bodyEl.setStyle("position", "relative");
25238
25239     this.active = null;
25240     this.activateDelegate = this.activate.createDelegate(this);
25241
25242     this.addEvents({
25243         /**
25244          * @event tabchange
25245          * Fires when the active tab changes
25246          * @param {Roo.TabPanel} this
25247          * @param {Roo.TabPanelItem} activePanel The new active tab
25248          */
25249         "tabchange": true,
25250         /**
25251          * @event beforetabchange
25252          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25253          * @param {Roo.TabPanel} this
25254          * @param {Object} e Set cancel to true on this object to cancel the tab change
25255          * @param {Roo.TabPanelItem} tab The tab being changed to
25256          */
25257         "beforetabchange" : true
25258     });
25259
25260     Roo.EventManager.onWindowResize(this.onResize, this);
25261     this.cpad = this.el.getPadding("lr");
25262     this.hiddenCount = 0;
25263
25264
25265     // toolbar on the tabbar support...
25266     if (this.toolbar) {
25267         var tcfg = this.toolbar;
25268         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25269         this.toolbar = new Roo.Toolbar(tcfg);
25270         if (Roo.isSafari) {
25271             var tbl = tcfg.container.child('table', true);
25272             tbl.setAttribute('width', '100%');
25273         }
25274         
25275     }
25276    
25277
25278
25279     Roo.TabPanel.superclass.constructor.call(this);
25280 };
25281
25282 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25283     /*
25284      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25285      */
25286     tabPosition : "top",
25287     /*
25288      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25289      */
25290     currentTabWidth : 0,
25291     /*
25292      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25293      */
25294     minTabWidth : 40,
25295     /*
25296      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25297      */
25298     maxTabWidth : 250,
25299     /*
25300      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25301      */
25302     preferredTabWidth : 175,
25303     /*
25304      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25305      */
25306     resizeTabs : false,
25307     /*
25308      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25309      */
25310     monitorResize : true,
25311     /*
25312      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25313      */
25314     toolbar : false,
25315
25316     /**
25317      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25318      * @param {String} id The id of the div to use <b>or create</b>
25319      * @param {String} text The text for the tab
25320      * @param {String} content (optional) Content to put in the TabPanelItem body
25321      * @param {Boolean} closable (optional) True to create a close icon on the tab
25322      * @return {Roo.TabPanelItem} The created TabPanelItem
25323      */
25324     addTab : function(id, text, content, closable){
25325         var item = new Roo.TabPanelItem(this, id, text, closable);
25326         this.addTabItem(item);
25327         if(content){
25328             item.setContent(content);
25329         }
25330         return item;
25331     },
25332
25333     /**
25334      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25335      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25336      * @return {Roo.TabPanelItem}
25337      */
25338     getTab : function(id){
25339         return this.items[id];
25340     },
25341
25342     /**
25343      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25344      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25345      */
25346     hideTab : function(id){
25347         var t = this.items[id];
25348         if(!t.isHidden()){
25349            t.setHidden(true);
25350            this.hiddenCount++;
25351            this.autoSizeTabs();
25352         }
25353     },
25354
25355     /**
25356      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25357      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25358      */
25359     unhideTab : function(id){
25360         var t = this.items[id];
25361         if(t.isHidden()){
25362            t.setHidden(false);
25363            this.hiddenCount--;
25364            this.autoSizeTabs();
25365         }
25366     },
25367
25368     /**
25369      * Adds an existing {@link Roo.TabPanelItem}.
25370      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25371      */
25372     addTabItem : function(item){
25373         this.items[item.id] = item;
25374         this.items.push(item);
25375         if(this.resizeTabs){
25376            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25377            this.autoSizeTabs();
25378         }else{
25379             item.autoSize();
25380         }
25381     },
25382
25383     /**
25384      * Removes a {@link Roo.TabPanelItem}.
25385      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25386      */
25387     removeTab : function(id){
25388         var items = this.items;
25389         var tab = items[id];
25390         if(!tab) { return; }
25391         var index = items.indexOf(tab);
25392         if(this.active == tab && items.length > 1){
25393             var newTab = this.getNextAvailable(index);
25394             if(newTab) {
25395                 newTab.activate();
25396             }
25397         }
25398         this.stripEl.dom.removeChild(tab.pnode.dom);
25399         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25400             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25401         }
25402         items.splice(index, 1);
25403         delete this.items[tab.id];
25404         tab.fireEvent("close", tab);
25405         tab.purgeListeners();
25406         this.autoSizeTabs();
25407     },
25408
25409     getNextAvailable : function(start){
25410         var items = this.items;
25411         var index = start;
25412         // look for a next tab that will slide over to
25413         // replace the one being removed
25414         while(index < items.length){
25415             var item = items[++index];
25416             if(item && !item.isHidden()){
25417                 return item;
25418             }
25419         }
25420         // if one isn't found select the previous tab (on the left)
25421         index = start;
25422         while(index >= 0){
25423             var item = items[--index];
25424             if(item && !item.isHidden()){
25425                 return item;
25426             }
25427         }
25428         return null;
25429     },
25430
25431     /**
25432      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25433      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25434      */
25435     disableTab : function(id){
25436         var tab = this.items[id];
25437         if(tab && this.active != tab){
25438             tab.disable();
25439         }
25440     },
25441
25442     /**
25443      * Enables a {@link Roo.TabPanelItem} that is disabled.
25444      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25445      */
25446     enableTab : function(id){
25447         var tab = this.items[id];
25448         tab.enable();
25449     },
25450
25451     /**
25452      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25453      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25454      * @return {Roo.TabPanelItem} The TabPanelItem.
25455      */
25456     activate : function(id){
25457         var tab = this.items[id];
25458         if(!tab){
25459             return null;
25460         }
25461         if(tab == this.active || tab.disabled){
25462             return tab;
25463         }
25464         var e = {};
25465         this.fireEvent("beforetabchange", this, e, tab);
25466         if(e.cancel !== true && !tab.disabled){
25467             if(this.active){
25468                 this.active.hide();
25469             }
25470             this.active = this.items[id];
25471             this.active.show();
25472             this.fireEvent("tabchange", this, this.active);
25473         }
25474         return tab;
25475     },
25476
25477     /**
25478      * Gets the active {@link Roo.TabPanelItem}.
25479      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25480      */
25481     getActiveTab : function(){
25482         return this.active;
25483     },
25484
25485     /**
25486      * Updates the tab body element to fit the height of the container element
25487      * for overflow scrolling
25488      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25489      */
25490     syncHeight : function(targetHeight){
25491         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25492         var bm = this.bodyEl.getMargins();
25493         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25494         this.bodyEl.setHeight(newHeight);
25495         return newHeight;
25496     },
25497
25498     onResize : function(){
25499         if(this.monitorResize){
25500             this.autoSizeTabs();
25501         }
25502     },
25503
25504     /**
25505      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25506      */
25507     beginUpdate : function(){
25508         this.updating = true;
25509     },
25510
25511     /**
25512      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25513      */
25514     endUpdate : function(){
25515         this.updating = false;
25516         this.autoSizeTabs();
25517     },
25518
25519     /**
25520      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25521      */
25522     autoSizeTabs : function(){
25523         var count = this.items.length;
25524         var vcount = count - this.hiddenCount;
25525         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25526         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25527         var availWidth = Math.floor(w / vcount);
25528         var b = this.stripBody;
25529         if(b.getWidth() > w){
25530             var tabs = this.items;
25531             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25532             if(availWidth < this.minTabWidth){
25533                 /*if(!this.sleft){    // incomplete scrolling code
25534                     this.createScrollButtons();
25535                 }
25536                 this.showScroll();
25537                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25538             }
25539         }else{
25540             if(this.currentTabWidth < this.preferredTabWidth){
25541                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25542             }
25543         }
25544     },
25545
25546     /**
25547      * Returns the number of tabs in this TabPanel.
25548      * @return {Number}
25549      */
25550      getCount : function(){
25551          return this.items.length;
25552      },
25553
25554     /**
25555      * Resizes all the tabs to the passed width
25556      * @param {Number} The new width
25557      */
25558     setTabWidth : function(width){
25559         this.currentTabWidth = width;
25560         for(var i = 0, len = this.items.length; i < len; i++) {
25561                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25562         }
25563     },
25564
25565     /**
25566      * Destroys this TabPanel
25567      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25568      */
25569     destroy : function(removeEl){
25570         Roo.EventManager.removeResizeListener(this.onResize, this);
25571         for(var i = 0, len = this.items.length; i < len; i++){
25572             this.items[i].purgeListeners();
25573         }
25574         if(removeEl === true){
25575             this.el.update("");
25576             this.el.remove();
25577         }
25578     }
25579 });
25580
25581 /**
25582  * @class Roo.TabPanelItem
25583  * @extends Roo.util.Observable
25584  * Represents an individual item (tab plus body) in a TabPanel.
25585  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25586  * @param {String} id The id of this TabPanelItem
25587  * @param {String} text The text for the tab of this TabPanelItem
25588  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25589  */
25590 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25591     /**
25592      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25593      * @type Roo.TabPanel
25594      */
25595     this.tabPanel = tabPanel;
25596     /**
25597      * The id for this TabPanelItem
25598      * @type String
25599      */
25600     this.id = id;
25601     /** @private */
25602     this.disabled = false;
25603     /** @private */
25604     this.text = text;
25605     /** @private */
25606     this.loaded = false;
25607     this.closable = closable;
25608
25609     /**
25610      * The body element for this TabPanelItem.
25611      * @type Roo.Element
25612      */
25613     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25614     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25615     this.bodyEl.setStyle("display", "block");
25616     this.bodyEl.setStyle("zoom", "1");
25617     this.hideAction();
25618
25619     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25620     /** @private */
25621     this.el = Roo.get(els.el, true);
25622     this.inner = Roo.get(els.inner, true);
25623     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25624     this.pnode = Roo.get(els.el.parentNode, true);
25625     this.el.on("mousedown", this.onTabMouseDown, this);
25626     this.el.on("click", this.onTabClick, this);
25627     /** @private */
25628     if(closable){
25629         var c = Roo.get(els.close, true);
25630         c.dom.title = this.closeText;
25631         c.addClassOnOver("close-over");
25632         c.on("click", this.closeClick, this);
25633      }
25634
25635     this.addEvents({
25636          /**
25637          * @event activate
25638          * Fires when this tab becomes the active tab.
25639          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25640          * @param {Roo.TabPanelItem} this
25641          */
25642         "activate": true,
25643         /**
25644          * @event beforeclose
25645          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25646          * @param {Roo.TabPanelItem} this
25647          * @param {Object} e Set cancel to true on this object to cancel the close.
25648          */
25649         "beforeclose": true,
25650         /**
25651          * @event close
25652          * Fires when this tab is closed.
25653          * @param {Roo.TabPanelItem} this
25654          */
25655          "close": true,
25656         /**
25657          * @event deactivate
25658          * Fires when this tab is no longer the active tab.
25659          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25660          * @param {Roo.TabPanelItem} this
25661          */
25662          "deactivate" : true
25663     });
25664     this.hidden = false;
25665
25666     Roo.TabPanelItem.superclass.constructor.call(this);
25667 };
25668
25669 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25670     purgeListeners : function(){
25671        Roo.util.Observable.prototype.purgeListeners.call(this);
25672        this.el.removeAllListeners();
25673     },
25674     /**
25675      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25676      */
25677     show : function(){
25678         this.pnode.addClass("on");
25679         this.showAction();
25680         if(Roo.isOpera){
25681             this.tabPanel.stripWrap.repaint();
25682         }
25683         this.fireEvent("activate", this.tabPanel, this);
25684     },
25685
25686     /**
25687      * Returns true if this tab is the active tab.
25688      * @return {Boolean}
25689      */
25690     isActive : function(){
25691         return this.tabPanel.getActiveTab() == this;
25692     },
25693
25694     /**
25695      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25696      */
25697     hide : function(){
25698         this.pnode.removeClass("on");
25699         this.hideAction();
25700         this.fireEvent("deactivate", this.tabPanel, this);
25701     },
25702
25703     hideAction : function(){
25704         this.bodyEl.hide();
25705         this.bodyEl.setStyle("position", "absolute");
25706         this.bodyEl.setLeft("-20000px");
25707         this.bodyEl.setTop("-20000px");
25708     },
25709
25710     showAction : function(){
25711         this.bodyEl.setStyle("position", "relative");
25712         this.bodyEl.setTop("");
25713         this.bodyEl.setLeft("");
25714         this.bodyEl.show();
25715     },
25716
25717     /**
25718      * Set the tooltip for the tab.
25719      * @param {String} tooltip The tab's tooltip
25720      */
25721     setTooltip : function(text){
25722         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25723             this.textEl.dom.qtip = text;
25724             this.textEl.dom.removeAttribute('title');
25725         }else{
25726             this.textEl.dom.title = text;
25727         }
25728     },
25729
25730     onTabClick : function(e){
25731         e.preventDefault();
25732         this.tabPanel.activate(this.id);
25733     },
25734
25735     onTabMouseDown : function(e){
25736         e.preventDefault();
25737         this.tabPanel.activate(this.id);
25738     },
25739
25740     getWidth : function(){
25741         return this.inner.getWidth();
25742     },
25743
25744     setWidth : function(width){
25745         var iwidth = width - this.pnode.getPadding("lr");
25746         this.inner.setWidth(iwidth);
25747         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25748         this.pnode.setWidth(width);
25749     },
25750
25751     /**
25752      * Show or hide the tab
25753      * @param {Boolean} hidden True to hide or false to show.
25754      */
25755     setHidden : function(hidden){
25756         this.hidden = hidden;
25757         this.pnode.setStyle("display", hidden ? "none" : "");
25758     },
25759
25760     /**
25761      * Returns true if this tab is "hidden"
25762      * @return {Boolean}
25763      */
25764     isHidden : function(){
25765         return this.hidden;
25766     },
25767
25768     /**
25769      * Returns the text for this tab
25770      * @return {String}
25771      */
25772     getText : function(){
25773         return this.text;
25774     },
25775
25776     autoSize : function(){
25777         //this.el.beginMeasure();
25778         this.textEl.setWidth(1);
25779         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25780         //this.el.endMeasure();
25781     },
25782
25783     /**
25784      * Sets the text for the tab (Note: this also sets the tooltip text)
25785      * @param {String} text The tab's text and tooltip
25786      */
25787     setText : function(text){
25788         this.text = text;
25789         this.textEl.update(text);
25790         this.setTooltip(text);
25791         if(!this.tabPanel.resizeTabs){
25792             this.autoSize();
25793         }
25794     },
25795     /**
25796      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25797      */
25798     activate : function(){
25799         this.tabPanel.activate(this.id);
25800     },
25801
25802     /**
25803      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25804      */
25805     disable : function(){
25806         if(this.tabPanel.active != this){
25807             this.disabled = true;
25808             this.pnode.addClass("disabled");
25809         }
25810     },
25811
25812     /**
25813      * Enables this TabPanelItem if it was previously disabled.
25814      */
25815     enable : function(){
25816         this.disabled = false;
25817         this.pnode.removeClass("disabled");
25818     },
25819
25820     /**
25821      * Sets the content for this TabPanelItem.
25822      * @param {String} content The content
25823      * @param {Boolean} loadScripts true to look for and load scripts
25824      */
25825     setContent : function(content, loadScripts){
25826         this.bodyEl.update(content, loadScripts);
25827     },
25828
25829     /**
25830      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25831      * @return {Roo.UpdateManager} The UpdateManager
25832      */
25833     getUpdateManager : function(){
25834         return this.bodyEl.getUpdateManager();
25835     },
25836
25837     /**
25838      * Set a URL to be used to load the content for this TabPanelItem.
25839      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25840      * @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)
25841      * @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)
25842      * @return {Roo.UpdateManager} The UpdateManager
25843      */
25844     setUrl : function(url, params, loadOnce){
25845         if(this.refreshDelegate){
25846             this.un('activate', this.refreshDelegate);
25847         }
25848         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25849         this.on("activate", this.refreshDelegate);
25850         return this.bodyEl.getUpdateManager();
25851     },
25852
25853     /** @private */
25854     _handleRefresh : function(url, params, loadOnce){
25855         if(!loadOnce || !this.loaded){
25856             var updater = this.bodyEl.getUpdateManager();
25857             updater.update(url, params, this._setLoaded.createDelegate(this));
25858         }
25859     },
25860
25861     /**
25862      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25863      *   Will fail silently if the setUrl method has not been called.
25864      *   This does not activate the panel, just updates its content.
25865      */
25866     refresh : function(){
25867         if(this.refreshDelegate){
25868            this.loaded = false;
25869            this.refreshDelegate();
25870         }
25871     },
25872
25873     /** @private */
25874     _setLoaded : function(){
25875         this.loaded = true;
25876     },
25877
25878     /** @private */
25879     closeClick : function(e){
25880         var o = {};
25881         e.stopEvent();
25882         this.fireEvent("beforeclose", this, o);
25883         if(o.cancel !== true){
25884             this.tabPanel.removeTab(this.id);
25885         }
25886     },
25887     /**
25888      * The text displayed in the tooltip for the close icon.
25889      * @type String
25890      */
25891     closeText : "Close this tab"
25892 });
25893
25894 /** @private */
25895 Roo.TabPanel.prototype.createStrip = function(container){
25896     var strip = document.createElement("div");
25897     strip.className = "x-tabs-wrap";
25898     container.appendChild(strip);
25899     return strip;
25900 };
25901 /** @private */
25902 Roo.TabPanel.prototype.createStripList = function(strip){
25903     // div wrapper for retard IE
25904     // returns the "tr" element.
25905     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
25906         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
25907         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
25908     return strip.firstChild.firstChild.firstChild.firstChild;
25909 };
25910 /** @private */
25911 Roo.TabPanel.prototype.createBody = function(container){
25912     var body = document.createElement("div");
25913     Roo.id(body, "tab-body");
25914     Roo.fly(body).addClass("x-tabs-body");
25915     container.appendChild(body);
25916     return body;
25917 };
25918 /** @private */
25919 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25920     var body = Roo.getDom(id);
25921     if(!body){
25922         body = document.createElement("div");
25923         body.id = id;
25924     }
25925     Roo.fly(body).addClass("x-tabs-item-body");
25926     bodyEl.insertBefore(body, bodyEl.firstChild);
25927     return body;
25928 };
25929 /** @private */
25930 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25931     var td = document.createElement("td");
25932     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
25933     //stripEl.appendChild(td);
25934     if(closable){
25935         td.className = "x-tabs-closable";
25936         if(!this.closeTpl){
25937             this.closeTpl = new Roo.Template(
25938                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25939                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25940                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25941             );
25942         }
25943         var el = this.closeTpl.overwrite(td, {"text": text});
25944         var close = el.getElementsByTagName("div")[0];
25945         var inner = el.getElementsByTagName("em")[0];
25946         return {"el": el, "close": close, "inner": inner};
25947     } else {
25948         if(!this.tabTpl){
25949             this.tabTpl = new Roo.Template(
25950                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25951                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25952             );
25953         }
25954         var el = this.tabTpl.overwrite(td, {"text": text});
25955         var inner = el.getElementsByTagName("em")[0];
25956         return {"el": el, "inner": inner};
25957     }
25958 };/*
25959  * Based on:
25960  * Ext JS Library 1.1.1
25961  * Copyright(c) 2006-2007, Ext JS, LLC.
25962  *
25963  * Originally Released Under LGPL - original licence link has changed is not relivant.
25964  *
25965  * Fork - LGPL
25966  * <script type="text/javascript">
25967  */
25968
25969 /**
25970  * @class Roo.Button
25971  * @extends Roo.util.Observable
25972  * Simple Button class
25973  * @cfg {String} text The button text
25974  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25975  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25976  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25977  * @cfg {Object} scope The scope of the handler
25978  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25979  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25980  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25981  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25982  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25983  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25984    applies if enableToggle = true)
25985  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25986  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25987   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25988  * @constructor
25989  * Create a new button
25990  * @param {Object} config The config object
25991  */
25992 Roo.Button = function(renderTo, config)
25993 {
25994     if (!config) {
25995         config = renderTo;
25996         renderTo = config.renderTo || false;
25997     }
25998     
25999     Roo.apply(this, config);
26000     this.addEvents({
26001         /**
26002              * @event click
26003              * Fires when this button is clicked
26004              * @param {Button} this
26005              * @param {EventObject} e The click event
26006              */
26007             "click" : true,
26008         /**
26009              * @event toggle
26010              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26011              * @param {Button} this
26012              * @param {Boolean} pressed
26013              */
26014             "toggle" : true,
26015         /**
26016              * @event mouseover
26017              * Fires when the mouse hovers over the button
26018              * @param {Button} this
26019              * @param {Event} e The event object
26020              */
26021         'mouseover' : true,
26022         /**
26023              * @event mouseout
26024              * Fires when the mouse exits the button
26025              * @param {Button} this
26026              * @param {Event} e The event object
26027              */
26028         'mouseout': true,
26029          /**
26030              * @event render
26031              * Fires when the button is rendered
26032              * @param {Button} this
26033              */
26034         'render': true
26035     });
26036     if(this.menu){
26037         this.menu = Roo.menu.MenuMgr.get(this.menu);
26038     }
26039     // register listeners first!!  - so render can be captured..
26040     Roo.util.Observable.call(this);
26041     if(renderTo){
26042         this.render(renderTo);
26043     }
26044     
26045   
26046 };
26047
26048 Roo.extend(Roo.Button, Roo.util.Observable, {
26049     /**
26050      * 
26051      */
26052     
26053     /**
26054      * Read-only. True if this button is hidden
26055      * @type Boolean
26056      */
26057     hidden : false,
26058     /**
26059      * Read-only. True if this button is disabled
26060      * @type Boolean
26061      */
26062     disabled : false,
26063     /**
26064      * Read-only. True if this button is pressed (only if enableToggle = true)
26065      * @type Boolean
26066      */
26067     pressed : false,
26068
26069     /**
26070      * @cfg {Number} tabIndex 
26071      * The DOM tabIndex for this button (defaults to undefined)
26072      */
26073     tabIndex : undefined,
26074
26075     /**
26076      * @cfg {Boolean} enableToggle
26077      * True to enable pressed/not pressed toggling (defaults to false)
26078      */
26079     enableToggle: false,
26080     /**
26081      * @cfg {Mixed} menu
26082      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26083      */
26084     menu : undefined,
26085     /**
26086      * @cfg {String} menuAlign
26087      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26088      */
26089     menuAlign : "tl-bl?",
26090
26091     /**
26092      * @cfg {String} iconCls
26093      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26094      */
26095     iconCls : undefined,
26096     /**
26097      * @cfg {String} type
26098      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26099      */
26100     type : 'button',
26101
26102     // private
26103     menuClassTarget: 'tr',
26104
26105     /**
26106      * @cfg {String} clickEvent
26107      * The type of event to map to the button's event handler (defaults to 'click')
26108      */
26109     clickEvent : 'click',
26110
26111     /**
26112      * @cfg {Boolean} handleMouseEvents
26113      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26114      */
26115     handleMouseEvents : true,
26116
26117     /**
26118      * @cfg {String} tooltipType
26119      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26120      */
26121     tooltipType : 'qtip',
26122
26123     /**
26124      * @cfg {String} cls
26125      * A CSS class to apply to the button's main element.
26126      */
26127     
26128     /**
26129      * @cfg {Roo.Template} template (Optional)
26130      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26131      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26132      * require code modifications if required elements (e.g. a button) aren't present.
26133      */
26134
26135     // private
26136     render : function(renderTo){
26137         var btn;
26138         if(this.hideParent){
26139             this.parentEl = Roo.get(renderTo);
26140         }
26141         if(!this.dhconfig){
26142             if(!this.template){
26143                 if(!Roo.Button.buttonTemplate){
26144                     // hideous table template
26145                     Roo.Button.buttonTemplate = new Roo.Template(
26146                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26147                         '<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>',
26148                         "</tr></tbody></table>");
26149                 }
26150                 this.template = Roo.Button.buttonTemplate;
26151             }
26152             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26153             var btnEl = btn.child("button:first");
26154             btnEl.on('focus', this.onFocus, this);
26155             btnEl.on('blur', this.onBlur, this);
26156             if(this.cls){
26157                 btn.addClass(this.cls);
26158             }
26159             if(this.icon){
26160                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26161             }
26162             if(this.iconCls){
26163                 btnEl.addClass(this.iconCls);
26164                 if(!this.cls){
26165                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26166                 }
26167             }
26168             if(this.tabIndex !== undefined){
26169                 btnEl.dom.tabIndex = this.tabIndex;
26170             }
26171             if(this.tooltip){
26172                 if(typeof this.tooltip == 'object'){
26173                     Roo.QuickTips.tips(Roo.apply({
26174                           target: btnEl.id
26175                     }, this.tooltip));
26176                 } else {
26177                     btnEl.dom[this.tooltipType] = this.tooltip;
26178                 }
26179             }
26180         }else{
26181             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26182         }
26183         this.el = btn;
26184         if(this.id){
26185             this.el.dom.id = this.el.id = this.id;
26186         }
26187         if(this.menu){
26188             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26189             this.menu.on("show", this.onMenuShow, this);
26190             this.menu.on("hide", this.onMenuHide, this);
26191         }
26192         btn.addClass("x-btn");
26193         if(Roo.isIE && !Roo.isIE7){
26194             this.autoWidth.defer(1, this);
26195         }else{
26196             this.autoWidth();
26197         }
26198         if(this.handleMouseEvents){
26199             btn.on("mouseover", this.onMouseOver, this);
26200             btn.on("mouseout", this.onMouseOut, this);
26201             btn.on("mousedown", this.onMouseDown, this);
26202         }
26203         btn.on(this.clickEvent, this.onClick, this);
26204         //btn.on("mouseup", this.onMouseUp, this);
26205         if(this.hidden){
26206             this.hide();
26207         }
26208         if(this.disabled){
26209             this.disable();
26210         }
26211         Roo.ButtonToggleMgr.register(this);
26212         if(this.pressed){
26213             this.el.addClass("x-btn-pressed");
26214         }
26215         if(this.repeat){
26216             var repeater = new Roo.util.ClickRepeater(btn,
26217                 typeof this.repeat == "object" ? this.repeat : {}
26218             );
26219             repeater.on("click", this.onClick,  this);
26220         }
26221         
26222         this.fireEvent('render', this);
26223         
26224     },
26225     /**
26226      * Returns the button's underlying element
26227      * @return {Roo.Element} The element
26228      */
26229     getEl : function(){
26230         return this.el;  
26231     },
26232     
26233     /**
26234      * Destroys this Button and removes any listeners.
26235      */
26236     destroy : function(){
26237         Roo.ButtonToggleMgr.unregister(this);
26238         this.el.removeAllListeners();
26239         this.purgeListeners();
26240         this.el.remove();
26241     },
26242
26243     // private
26244     autoWidth : function(){
26245         if(this.el){
26246             this.el.setWidth("auto");
26247             if(Roo.isIE7 && Roo.isStrict){
26248                 var ib = this.el.child('button');
26249                 if(ib && ib.getWidth() > 20){
26250                     ib.clip();
26251                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26252                 }
26253             }
26254             if(this.minWidth){
26255                 if(this.hidden){
26256                     this.el.beginMeasure();
26257                 }
26258                 if(this.el.getWidth() < this.minWidth){
26259                     this.el.setWidth(this.minWidth);
26260                 }
26261                 if(this.hidden){
26262                     this.el.endMeasure();
26263                 }
26264             }
26265         }
26266     },
26267
26268     /**
26269      * Assigns this button's click handler
26270      * @param {Function} handler The function to call when the button is clicked
26271      * @param {Object} scope (optional) Scope for the function passed in
26272      */
26273     setHandler : function(handler, scope){
26274         this.handler = handler;
26275         this.scope = scope;  
26276     },
26277     
26278     /**
26279      * Sets this button's text
26280      * @param {String} text The button text
26281      */
26282     setText : function(text){
26283         this.text = text;
26284         if(this.el){
26285             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26286         }
26287         this.autoWidth();
26288     },
26289     
26290     /**
26291      * Gets the text for this button
26292      * @return {String} The button text
26293      */
26294     getText : function(){
26295         return this.text;  
26296     },
26297     
26298     /**
26299      * Show this button
26300      */
26301     show: function(){
26302         this.hidden = false;
26303         if(this.el){
26304             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26305         }
26306     },
26307     
26308     /**
26309      * Hide this button
26310      */
26311     hide: function(){
26312         this.hidden = true;
26313         if(this.el){
26314             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26315         }
26316     },
26317     
26318     /**
26319      * Convenience function for boolean show/hide
26320      * @param {Boolean} visible True to show, false to hide
26321      */
26322     setVisible: function(visible){
26323         if(visible) {
26324             this.show();
26325         }else{
26326             this.hide();
26327         }
26328     },
26329     
26330     /**
26331      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26332      * @param {Boolean} state (optional) Force a particular state
26333      */
26334     toggle : function(state){
26335         state = state === undefined ? !this.pressed : state;
26336         if(state != this.pressed){
26337             if(state){
26338                 this.el.addClass("x-btn-pressed");
26339                 this.pressed = true;
26340                 this.fireEvent("toggle", this, true);
26341             }else{
26342                 this.el.removeClass("x-btn-pressed");
26343                 this.pressed = false;
26344                 this.fireEvent("toggle", this, false);
26345             }
26346             if(this.toggleHandler){
26347                 this.toggleHandler.call(this.scope || this, this, state);
26348             }
26349         }
26350     },
26351     
26352     /**
26353      * Focus the button
26354      */
26355     focus : function(){
26356         this.el.child('button:first').focus();
26357     },
26358     
26359     /**
26360      * Disable this button
26361      */
26362     disable : function(){
26363         if(this.el){
26364             this.el.addClass("x-btn-disabled");
26365         }
26366         this.disabled = true;
26367     },
26368     
26369     /**
26370      * Enable this button
26371      */
26372     enable : function(){
26373         if(this.el){
26374             this.el.removeClass("x-btn-disabled");
26375         }
26376         this.disabled = false;
26377     },
26378
26379     /**
26380      * Convenience function for boolean enable/disable
26381      * @param {Boolean} enabled True to enable, false to disable
26382      */
26383     setDisabled : function(v){
26384         this[v !== true ? "enable" : "disable"]();
26385     },
26386
26387     // private
26388     onClick : function(e){
26389         if(e){
26390             e.preventDefault();
26391         }
26392         if(e.button != 0){
26393             return;
26394         }
26395         if(!this.disabled){
26396             if(this.enableToggle){
26397                 this.toggle();
26398             }
26399             if(this.menu && !this.menu.isVisible()){
26400                 this.menu.show(this.el, this.menuAlign);
26401             }
26402             this.fireEvent("click", this, e);
26403             if(this.handler){
26404                 this.el.removeClass("x-btn-over");
26405                 this.handler.call(this.scope || this, this, e);
26406             }
26407         }
26408     },
26409     // private
26410     onMouseOver : function(e){
26411         if(!this.disabled){
26412             this.el.addClass("x-btn-over");
26413             this.fireEvent('mouseover', this, e);
26414         }
26415     },
26416     // private
26417     onMouseOut : function(e){
26418         if(!e.within(this.el,  true)){
26419             this.el.removeClass("x-btn-over");
26420             this.fireEvent('mouseout', this, e);
26421         }
26422     },
26423     // private
26424     onFocus : function(e){
26425         if(!this.disabled){
26426             this.el.addClass("x-btn-focus");
26427         }
26428     },
26429     // private
26430     onBlur : function(e){
26431         this.el.removeClass("x-btn-focus");
26432     },
26433     // private
26434     onMouseDown : function(e){
26435         if(!this.disabled && e.button == 0){
26436             this.el.addClass("x-btn-click");
26437             Roo.get(document).on('mouseup', this.onMouseUp, this);
26438         }
26439     },
26440     // private
26441     onMouseUp : function(e){
26442         if(e.button == 0){
26443             this.el.removeClass("x-btn-click");
26444             Roo.get(document).un('mouseup', this.onMouseUp, this);
26445         }
26446     },
26447     // private
26448     onMenuShow : function(e){
26449         this.el.addClass("x-btn-menu-active");
26450     },
26451     // private
26452     onMenuHide : function(e){
26453         this.el.removeClass("x-btn-menu-active");
26454     }   
26455 });
26456
26457 // Private utility class used by Button
26458 Roo.ButtonToggleMgr = function(){
26459    var groups = {};
26460    
26461    function toggleGroup(btn, state){
26462        if(state){
26463            var g = groups[btn.toggleGroup];
26464            for(var i = 0, l = g.length; i < l; i++){
26465                if(g[i] != btn){
26466                    g[i].toggle(false);
26467                }
26468            }
26469        }
26470    }
26471    
26472    return {
26473        register : function(btn){
26474            if(!btn.toggleGroup){
26475                return;
26476            }
26477            var g = groups[btn.toggleGroup];
26478            if(!g){
26479                g = groups[btn.toggleGroup] = [];
26480            }
26481            g.push(btn);
26482            btn.on("toggle", toggleGroup);
26483        },
26484        
26485        unregister : function(btn){
26486            if(!btn.toggleGroup){
26487                return;
26488            }
26489            var g = groups[btn.toggleGroup];
26490            if(g){
26491                g.remove(btn);
26492                btn.un("toggle", toggleGroup);
26493            }
26494        }
26495    };
26496 }();/*
26497  * Based on:
26498  * Ext JS Library 1.1.1
26499  * Copyright(c) 2006-2007, Ext JS, LLC.
26500  *
26501  * Originally Released Under LGPL - original licence link has changed is not relivant.
26502  *
26503  * Fork - LGPL
26504  * <script type="text/javascript">
26505  */
26506  
26507 /**
26508  * @class Roo.SplitButton
26509  * @extends Roo.Button
26510  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26511  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26512  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26513  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26514  * @cfg {String} arrowTooltip The title attribute of the arrow
26515  * @constructor
26516  * Create a new menu button
26517  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26518  * @param {Object} config The config object
26519  */
26520 Roo.SplitButton = function(renderTo, config){
26521     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26522     /**
26523      * @event arrowclick
26524      * Fires when this button's arrow is clicked
26525      * @param {SplitButton} this
26526      * @param {EventObject} e The click event
26527      */
26528     this.addEvents({"arrowclick":true});
26529 };
26530
26531 Roo.extend(Roo.SplitButton, Roo.Button, {
26532     render : function(renderTo){
26533         // this is one sweet looking template!
26534         var tpl = new Roo.Template(
26535             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26536             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26537             '<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>',
26538             "</tbody></table></td><td>",
26539             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26540             '<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>',
26541             "</tbody></table></td></tr></table>"
26542         );
26543         var btn = tpl.append(renderTo, [this.text, this.type], true);
26544         var btnEl = btn.child("button");
26545         if(this.cls){
26546             btn.addClass(this.cls);
26547         }
26548         if(this.icon){
26549             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26550         }
26551         if(this.iconCls){
26552             btnEl.addClass(this.iconCls);
26553             if(!this.cls){
26554                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26555             }
26556         }
26557         this.el = btn;
26558         if(this.handleMouseEvents){
26559             btn.on("mouseover", this.onMouseOver, this);
26560             btn.on("mouseout", this.onMouseOut, this);
26561             btn.on("mousedown", this.onMouseDown, this);
26562             btn.on("mouseup", this.onMouseUp, this);
26563         }
26564         btn.on(this.clickEvent, this.onClick, this);
26565         if(this.tooltip){
26566             if(typeof this.tooltip == 'object'){
26567                 Roo.QuickTips.tips(Roo.apply({
26568                       target: btnEl.id
26569                 }, this.tooltip));
26570             } else {
26571                 btnEl.dom[this.tooltipType] = this.tooltip;
26572             }
26573         }
26574         if(this.arrowTooltip){
26575             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26576         }
26577         if(this.hidden){
26578             this.hide();
26579         }
26580         if(this.disabled){
26581             this.disable();
26582         }
26583         if(this.pressed){
26584             this.el.addClass("x-btn-pressed");
26585         }
26586         if(Roo.isIE && !Roo.isIE7){
26587             this.autoWidth.defer(1, this);
26588         }else{
26589             this.autoWidth();
26590         }
26591         if(this.menu){
26592             this.menu.on("show", this.onMenuShow, this);
26593             this.menu.on("hide", this.onMenuHide, this);
26594         }
26595         this.fireEvent('render', this);
26596     },
26597
26598     // private
26599     autoWidth : function(){
26600         if(this.el){
26601             var tbl = this.el.child("table:first");
26602             var tbl2 = this.el.child("table:last");
26603             this.el.setWidth("auto");
26604             tbl.setWidth("auto");
26605             if(Roo.isIE7 && Roo.isStrict){
26606                 var ib = this.el.child('button:first');
26607                 if(ib && ib.getWidth() > 20){
26608                     ib.clip();
26609                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26610                 }
26611             }
26612             if(this.minWidth){
26613                 if(this.hidden){
26614                     this.el.beginMeasure();
26615                 }
26616                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26617                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26618                 }
26619                 if(this.hidden){
26620                     this.el.endMeasure();
26621                 }
26622             }
26623             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26624         } 
26625     },
26626     /**
26627      * Sets this button's click handler
26628      * @param {Function} handler The function to call when the button is clicked
26629      * @param {Object} scope (optional) Scope for the function passed above
26630      */
26631     setHandler : function(handler, scope){
26632         this.handler = handler;
26633         this.scope = scope;  
26634     },
26635     
26636     /**
26637      * Sets this button's arrow click handler
26638      * @param {Function} handler The function to call when the arrow is clicked
26639      * @param {Object} scope (optional) Scope for the function passed above
26640      */
26641     setArrowHandler : function(handler, scope){
26642         this.arrowHandler = handler;
26643         this.scope = scope;  
26644     },
26645     
26646     /**
26647      * Focus the button
26648      */
26649     focus : function(){
26650         if(this.el){
26651             this.el.child("button:first").focus();
26652         }
26653     },
26654
26655     // private
26656     onClick : function(e){
26657         e.preventDefault();
26658         if(!this.disabled){
26659             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26660                 if(this.menu && !this.menu.isVisible()){
26661                     this.menu.show(this.el, this.menuAlign);
26662                 }
26663                 this.fireEvent("arrowclick", this, e);
26664                 if(this.arrowHandler){
26665                     this.arrowHandler.call(this.scope || this, this, e);
26666                 }
26667             }else{
26668                 this.fireEvent("click", this, e);
26669                 if(this.handler){
26670                     this.handler.call(this.scope || this, this, e);
26671                 }
26672             }
26673         }
26674     },
26675     // private
26676     onMouseDown : function(e){
26677         if(!this.disabled){
26678             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26679         }
26680     },
26681     // private
26682     onMouseUp : function(e){
26683         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26684     }   
26685 });
26686
26687
26688 // backwards compat
26689 Roo.MenuButton = Roo.SplitButton;/*
26690  * Based on:
26691  * Ext JS Library 1.1.1
26692  * Copyright(c) 2006-2007, Ext JS, LLC.
26693  *
26694  * Originally Released Under LGPL - original licence link has changed is not relivant.
26695  *
26696  * Fork - LGPL
26697  * <script type="text/javascript">
26698  */
26699
26700 /**
26701  * @class Roo.Toolbar
26702  * Basic Toolbar class.
26703  * @constructor
26704  * Creates a new Toolbar
26705  * @param {Object} config The config object
26706  */ 
26707 Roo.Toolbar = function(container, buttons, config)
26708 {
26709     /// old consturctor format still supported..
26710     if(container instanceof Array){ // omit the container for later rendering
26711         buttons = container;
26712         config = buttons;
26713         container = null;
26714     }
26715     if (typeof(container) == 'object' && container.xtype) {
26716         config = container;
26717         container = config.container;
26718         buttons = config.buttons; // not really - use items!!
26719     }
26720     var xitems = [];
26721     if (config && config.items) {
26722         xitems = config.items;
26723         delete config.items;
26724     }
26725     Roo.apply(this, config);
26726     this.buttons = buttons;
26727     
26728     if(container){
26729         this.render(container);
26730     }
26731     Roo.each(xitems, function(b) {
26732         this.add(b);
26733     }, this);
26734     
26735 };
26736
26737 Roo.Toolbar.prototype = {
26738     /**
26739      * @cfg {Roo.data.Store} items
26740      * array of button configs or elements to add
26741      */
26742     
26743     /**
26744      * @cfg {String/HTMLElement/Element} container
26745      * The id or element that will contain the toolbar
26746      */
26747     // private
26748     render : function(ct){
26749         this.el = Roo.get(ct);
26750         if(this.cls){
26751             this.el.addClass(this.cls);
26752         }
26753         // using a table allows for vertical alignment
26754         // 100% width is needed by Safari...
26755         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26756         this.tr = this.el.child("tr", true);
26757         var autoId = 0;
26758         this.items = new Roo.util.MixedCollection(false, function(o){
26759             return o.id || ("item" + (++autoId));
26760         });
26761         if(this.buttons){
26762             this.add.apply(this, this.buttons);
26763             delete this.buttons;
26764         }
26765     },
26766
26767     /**
26768      * Adds element(s) to the toolbar -- this function takes a variable number of 
26769      * arguments of mixed type and adds them to the toolbar.
26770      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26771      * <ul>
26772      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26773      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26774      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26775      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26776      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26777      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26778      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26779      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26780      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26781      * </ul>
26782      * @param {Mixed} arg2
26783      * @param {Mixed} etc.
26784      */
26785     add : function(){
26786         var a = arguments, l = a.length;
26787         for(var i = 0; i < l; i++){
26788             this._add(a[i]);
26789         }
26790     },
26791     // private..
26792     _add : function(el) {
26793         
26794         if (el.xtype) {
26795             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26796         }
26797         
26798         if (el.applyTo){ // some kind of form field
26799             return this.addField(el);
26800         } 
26801         if (el.render){ // some kind of Toolbar.Item
26802             return this.addItem(el);
26803         }
26804         if (typeof el == "string"){ // string
26805             if(el == "separator" || el == "-"){
26806                 return this.addSeparator();
26807             }
26808             if (el == " "){
26809                 return this.addSpacer();
26810             }
26811             if(el == "->"){
26812                 return this.addFill();
26813             }
26814             return this.addText(el);
26815             
26816         }
26817         if(el.tagName){ // element
26818             return this.addElement(el);
26819         }
26820         if(typeof el == "object"){ // must be button config?
26821             return this.addButton(el);
26822         }
26823         // and now what?!?!
26824         return false;
26825         
26826     },
26827     
26828     /**
26829      * Add an Xtype element
26830      * @param {Object} xtype Xtype Object
26831      * @return {Object} created Object
26832      */
26833     addxtype : function(e){
26834         return this.add(e);  
26835     },
26836     
26837     /**
26838      * Returns the Element for this toolbar.
26839      * @return {Roo.Element}
26840      */
26841     getEl : function(){
26842         return this.el;  
26843     },
26844     
26845     /**
26846      * Adds a separator
26847      * @return {Roo.Toolbar.Item} The separator item
26848      */
26849     addSeparator : function(){
26850         return this.addItem(new Roo.Toolbar.Separator());
26851     },
26852
26853     /**
26854      * Adds a spacer element
26855      * @return {Roo.Toolbar.Spacer} The spacer item
26856      */
26857     addSpacer : function(){
26858         return this.addItem(new Roo.Toolbar.Spacer());
26859     },
26860
26861     /**
26862      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26863      * @return {Roo.Toolbar.Fill} The fill item
26864      */
26865     addFill : function(){
26866         return this.addItem(new Roo.Toolbar.Fill());
26867     },
26868
26869     /**
26870      * Adds any standard HTML element to the toolbar
26871      * @param {String/HTMLElement/Element} el The element or id of the element to add
26872      * @return {Roo.Toolbar.Item} The element's item
26873      */
26874     addElement : function(el){
26875         return this.addItem(new Roo.Toolbar.Item(el));
26876     },
26877     /**
26878      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26879      * @type Roo.util.MixedCollection  
26880      */
26881     items : false,
26882      
26883     /**
26884      * Adds any Toolbar.Item or subclass
26885      * @param {Roo.Toolbar.Item} item
26886      * @return {Roo.Toolbar.Item} The item
26887      */
26888     addItem : function(item){
26889         var td = this.nextBlock();
26890         item.render(td);
26891         this.items.add(item);
26892         return item;
26893     },
26894     
26895     /**
26896      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26897      * @param {Object/Array} config A button config or array of configs
26898      * @return {Roo.Toolbar.Button/Array}
26899      */
26900     addButton : function(config){
26901         if(config instanceof Array){
26902             var buttons = [];
26903             for(var i = 0, len = config.length; i < len; i++) {
26904                 buttons.push(this.addButton(config[i]));
26905             }
26906             return buttons;
26907         }
26908         var b = config;
26909         if(!(config instanceof Roo.Toolbar.Button)){
26910             b = config.split ?
26911                 new Roo.Toolbar.SplitButton(config) :
26912                 new Roo.Toolbar.Button(config);
26913         }
26914         var td = this.nextBlock();
26915         b.render(td);
26916         this.items.add(b);
26917         return b;
26918     },
26919     
26920     /**
26921      * Adds text to the toolbar
26922      * @param {String} text The text to add
26923      * @return {Roo.Toolbar.Item} The element's item
26924      */
26925     addText : function(text){
26926         return this.addItem(new Roo.Toolbar.TextItem(text));
26927     },
26928     
26929     /**
26930      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26931      * @param {Number} index The index where the item is to be inserted
26932      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26933      * @return {Roo.Toolbar.Button/Item}
26934      */
26935     insertButton : function(index, item){
26936         if(item instanceof Array){
26937             var buttons = [];
26938             for(var i = 0, len = item.length; i < len; i++) {
26939                buttons.push(this.insertButton(index + i, item[i]));
26940             }
26941             return buttons;
26942         }
26943         if (!(item instanceof Roo.Toolbar.Button)){
26944            item = new Roo.Toolbar.Button(item);
26945         }
26946         var td = document.createElement("td");
26947         this.tr.insertBefore(td, this.tr.childNodes[index]);
26948         item.render(td);
26949         this.items.insert(index, item);
26950         return item;
26951     },
26952     
26953     /**
26954      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26955      * @param {Object} config
26956      * @return {Roo.Toolbar.Item} The element's item
26957      */
26958     addDom : function(config, returnEl){
26959         var td = this.nextBlock();
26960         Roo.DomHelper.overwrite(td, config);
26961         var ti = new Roo.Toolbar.Item(td.firstChild);
26962         ti.render(td);
26963         this.items.add(ti);
26964         return ti;
26965     },
26966
26967     /**
26968      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26969      * @type Roo.util.MixedCollection  
26970      */
26971     fields : false,
26972     
26973     /**
26974      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26975      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26976      * @param {Roo.form.Field} field
26977      * @return {Roo.ToolbarItem}
26978      */
26979      
26980       
26981     addField : function(field) {
26982         if (!this.fields) {
26983             var autoId = 0;
26984             this.fields = new Roo.util.MixedCollection(false, function(o){
26985                 return o.id || ("item" + (++autoId));
26986             });
26987
26988         }
26989         
26990         var td = this.nextBlock();
26991         field.render(td);
26992         var ti = new Roo.Toolbar.Item(td.firstChild);
26993         ti.render(td);
26994         this.items.add(ti);
26995         this.fields.add(field);
26996         return ti;
26997     },
26998     /**
26999      * Hide the toolbar
27000      * @method hide
27001      */
27002      
27003       
27004     hide : function()
27005     {
27006         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27007         this.el.child('div').hide();
27008     },
27009     /**
27010      * Show the toolbar
27011      * @method show
27012      */
27013     show : function()
27014     {
27015         this.el.child('div').show();
27016     },
27017       
27018     // private
27019     nextBlock : function(){
27020         var td = document.createElement("td");
27021         this.tr.appendChild(td);
27022         return td;
27023     },
27024
27025     // private
27026     destroy : function(){
27027         if(this.items){ // rendered?
27028             Roo.destroy.apply(Roo, this.items.items);
27029         }
27030         if(this.fields){ // rendered?
27031             Roo.destroy.apply(Roo, this.fields.items);
27032         }
27033         Roo.Element.uncache(this.el, this.tr);
27034     }
27035 };
27036
27037 /**
27038  * @class Roo.Toolbar.Item
27039  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27040  * @constructor
27041  * Creates a new Item
27042  * @param {HTMLElement} el 
27043  */
27044 Roo.Toolbar.Item = function(el){
27045     this.el = Roo.getDom(el);
27046     this.id = Roo.id(this.el);
27047     this.hidden = false;
27048 };
27049
27050 Roo.Toolbar.Item.prototype = {
27051     
27052     /**
27053      * Get this item's HTML Element
27054      * @return {HTMLElement}
27055      */
27056     getEl : function(){
27057        return this.el;  
27058     },
27059
27060     // private
27061     render : function(td){
27062         this.td = td;
27063         td.appendChild(this.el);
27064     },
27065     
27066     /**
27067      * Removes and destroys this item.
27068      */
27069     destroy : function(){
27070         this.td.parentNode.removeChild(this.td);
27071     },
27072     
27073     /**
27074      * Shows this item.
27075      */
27076     show: function(){
27077         this.hidden = false;
27078         this.td.style.display = "";
27079     },
27080     
27081     /**
27082      * Hides this item.
27083      */
27084     hide: function(){
27085         this.hidden = true;
27086         this.td.style.display = "none";
27087     },
27088     
27089     /**
27090      * Convenience function for boolean show/hide.
27091      * @param {Boolean} visible true to show/false to hide
27092      */
27093     setVisible: function(visible){
27094         if(visible) {
27095             this.show();
27096         }else{
27097             this.hide();
27098         }
27099     },
27100     
27101     /**
27102      * Try to focus this item.
27103      */
27104     focus : function(){
27105         Roo.fly(this.el).focus();
27106     },
27107     
27108     /**
27109      * Disables this item.
27110      */
27111     disable : function(){
27112         Roo.fly(this.td).addClass("x-item-disabled");
27113         this.disabled = true;
27114         this.el.disabled = true;
27115     },
27116     
27117     /**
27118      * Enables this item.
27119      */
27120     enable : function(){
27121         Roo.fly(this.td).removeClass("x-item-disabled");
27122         this.disabled = false;
27123         this.el.disabled = false;
27124     }
27125 };
27126
27127
27128 /**
27129  * @class Roo.Toolbar.Separator
27130  * @extends Roo.Toolbar.Item
27131  * A simple toolbar separator class
27132  * @constructor
27133  * Creates a new Separator
27134  */
27135 Roo.Toolbar.Separator = function(){
27136     var s = document.createElement("span");
27137     s.className = "ytb-sep";
27138     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27139 };
27140 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27141     enable:Roo.emptyFn,
27142     disable:Roo.emptyFn,
27143     focus:Roo.emptyFn
27144 });
27145
27146 /**
27147  * @class Roo.Toolbar.Spacer
27148  * @extends Roo.Toolbar.Item
27149  * A simple element that adds extra horizontal space to a toolbar.
27150  * @constructor
27151  * Creates a new Spacer
27152  */
27153 Roo.Toolbar.Spacer = function(){
27154     var s = document.createElement("div");
27155     s.className = "ytb-spacer";
27156     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27157 };
27158 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27159     enable:Roo.emptyFn,
27160     disable:Roo.emptyFn,
27161     focus:Roo.emptyFn
27162 });
27163
27164 /**
27165  * @class Roo.Toolbar.Fill
27166  * @extends Roo.Toolbar.Spacer
27167  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27168  * @constructor
27169  * Creates a new Spacer
27170  */
27171 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27172     // private
27173     render : function(td){
27174         td.style.width = '100%';
27175         Roo.Toolbar.Fill.superclass.render.call(this, td);
27176     }
27177 });
27178
27179 /**
27180  * @class Roo.Toolbar.TextItem
27181  * @extends Roo.Toolbar.Item
27182  * A simple class that renders text directly into a toolbar.
27183  * @constructor
27184  * Creates a new TextItem
27185  * @param {String} text
27186  */
27187 Roo.Toolbar.TextItem = function(text){
27188     if (typeof(text) == 'object') {
27189         text = text.text;
27190     }
27191     var s = document.createElement("span");
27192     s.className = "ytb-text";
27193     s.innerHTML = text;
27194     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27195 };
27196 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27197     enable:Roo.emptyFn,
27198     disable:Roo.emptyFn,
27199     focus:Roo.emptyFn
27200 });
27201
27202 /**
27203  * @class Roo.Toolbar.Button
27204  * @extends Roo.Button
27205  * A button that renders into a toolbar.
27206  * @constructor
27207  * Creates a new Button
27208  * @param {Object} config A standard {@link Roo.Button} config object
27209  */
27210 Roo.Toolbar.Button = function(config){
27211     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27212 };
27213 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27214     render : function(td){
27215         this.td = td;
27216         Roo.Toolbar.Button.superclass.render.call(this, td);
27217     },
27218     
27219     /**
27220      * Removes and destroys this button
27221      */
27222     destroy : function(){
27223         Roo.Toolbar.Button.superclass.destroy.call(this);
27224         this.td.parentNode.removeChild(this.td);
27225     },
27226     
27227     /**
27228      * Shows this button
27229      */
27230     show: function(){
27231         this.hidden = false;
27232         this.td.style.display = "";
27233     },
27234     
27235     /**
27236      * Hides this button
27237      */
27238     hide: function(){
27239         this.hidden = true;
27240         this.td.style.display = "none";
27241     },
27242
27243     /**
27244      * Disables this item
27245      */
27246     disable : function(){
27247         Roo.fly(this.td).addClass("x-item-disabled");
27248         this.disabled = true;
27249     },
27250
27251     /**
27252      * Enables this item
27253      */
27254     enable : function(){
27255         Roo.fly(this.td).removeClass("x-item-disabled");
27256         this.disabled = false;
27257     }
27258 });
27259 // backwards compat
27260 Roo.ToolbarButton = Roo.Toolbar.Button;
27261
27262 /**
27263  * @class Roo.Toolbar.SplitButton
27264  * @extends Roo.SplitButton
27265  * A menu button that renders into a toolbar.
27266  * @constructor
27267  * Creates a new SplitButton
27268  * @param {Object} config A standard {@link Roo.SplitButton} config object
27269  */
27270 Roo.Toolbar.SplitButton = function(config){
27271     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27272 };
27273 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27274     render : function(td){
27275         this.td = td;
27276         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27277     },
27278     
27279     /**
27280      * Removes and destroys this button
27281      */
27282     destroy : function(){
27283         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27284         this.td.parentNode.removeChild(this.td);
27285     },
27286     
27287     /**
27288      * Shows this button
27289      */
27290     show: function(){
27291         this.hidden = false;
27292         this.td.style.display = "";
27293     },
27294     
27295     /**
27296      * Hides this button
27297      */
27298     hide: function(){
27299         this.hidden = true;
27300         this.td.style.display = "none";
27301     }
27302 });
27303
27304 // backwards compat
27305 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27306  * Based on:
27307  * Ext JS Library 1.1.1
27308  * Copyright(c) 2006-2007, Ext JS, LLC.
27309  *
27310  * Originally Released Under LGPL - original licence link has changed is not relivant.
27311  *
27312  * Fork - LGPL
27313  * <script type="text/javascript">
27314  */
27315  
27316 /**
27317  * @class Roo.PagingToolbar
27318  * @extends Roo.Toolbar
27319  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27320  * @constructor
27321  * Create a new PagingToolbar
27322  * @param {Object} config The config object
27323  */
27324 Roo.PagingToolbar = function(el, ds, config)
27325 {
27326     // old args format still supported... - xtype is prefered..
27327     if (typeof(el) == 'object' && el.xtype) {
27328         // created from xtype...
27329         config = el;
27330         ds = el.dataSource;
27331         el = config.container;
27332     }
27333     var items = [];
27334     if (config.items) {
27335         items = config.items;
27336         config.items = [];
27337     }
27338     
27339     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27340     this.ds = ds;
27341     this.cursor = 0;
27342     this.renderButtons(this.el);
27343     this.bind(ds);
27344     
27345     // supprot items array.
27346    
27347     Roo.each(items, function(e) {
27348         this.add(Roo.factory(e));
27349     },this);
27350     
27351 };
27352
27353 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27354     /**
27355      * @cfg {Roo.data.Store} dataSource
27356      * The underlying data store providing the paged data
27357      */
27358     /**
27359      * @cfg {String/HTMLElement/Element} container
27360      * container The id or element that will contain the toolbar
27361      */
27362     /**
27363      * @cfg {Boolean} displayInfo
27364      * True to display the displayMsg (defaults to false)
27365      */
27366     /**
27367      * @cfg {Number} pageSize
27368      * The number of records to display per page (defaults to 20)
27369      */
27370     pageSize: 20,
27371     /**
27372      * @cfg {String} displayMsg
27373      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27374      */
27375     displayMsg : 'Displaying {0} - {1} of {2}',
27376     /**
27377      * @cfg {String} emptyMsg
27378      * The message to display when no records are found (defaults to "No data to display")
27379      */
27380     emptyMsg : 'No data to display',
27381     /**
27382      * Customizable piece of the default paging text (defaults to "Page")
27383      * @type String
27384      */
27385     beforePageText : "Page",
27386     /**
27387      * Customizable piece of the default paging text (defaults to "of %0")
27388      * @type String
27389      */
27390     afterPageText : "of {0}",
27391     /**
27392      * Customizable piece of the default paging text (defaults to "First Page")
27393      * @type String
27394      */
27395     firstText : "First Page",
27396     /**
27397      * Customizable piece of the default paging text (defaults to "Previous Page")
27398      * @type String
27399      */
27400     prevText : "Previous Page",
27401     /**
27402      * Customizable piece of the default paging text (defaults to "Next Page")
27403      * @type String
27404      */
27405     nextText : "Next Page",
27406     /**
27407      * Customizable piece of the default paging text (defaults to "Last Page")
27408      * @type String
27409      */
27410     lastText : "Last Page",
27411     /**
27412      * Customizable piece of the default paging text (defaults to "Refresh")
27413      * @type String
27414      */
27415     refreshText : "Refresh",
27416
27417     // private
27418     renderButtons : function(el){
27419         Roo.PagingToolbar.superclass.render.call(this, el);
27420         this.first = this.addButton({
27421             tooltip: this.firstText,
27422             cls: "x-btn-icon x-grid-page-first",
27423             disabled: true,
27424             handler: this.onClick.createDelegate(this, ["first"])
27425         });
27426         this.prev = this.addButton({
27427             tooltip: this.prevText,
27428             cls: "x-btn-icon x-grid-page-prev",
27429             disabled: true,
27430             handler: this.onClick.createDelegate(this, ["prev"])
27431         });
27432         //this.addSeparator();
27433         this.add(this.beforePageText);
27434         this.field = Roo.get(this.addDom({
27435            tag: "input",
27436            type: "text",
27437            size: "3",
27438            value: "1",
27439            cls: "x-grid-page-number"
27440         }).el);
27441         this.field.on("keydown", this.onPagingKeydown, this);
27442         this.field.on("focus", function(){this.dom.select();});
27443         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27444         this.field.setHeight(18);
27445         //this.addSeparator();
27446         this.next = this.addButton({
27447             tooltip: this.nextText,
27448             cls: "x-btn-icon x-grid-page-next",
27449             disabled: true,
27450             handler: this.onClick.createDelegate(this, ["next"])
27451         });
27452         this.last = this.addButton({
27453             tooltip: this.lastText,
27454             cls: "x-btn-icon x-grid-page-last",
27455             disabled: true,
27456             handler: this.onClick.createDelegate(this, ["last"])
27457         });
27458         //this.addSeparator();
27459         this.loading = this.addButton({
27460             tooltip: this.refreshText,
27461             cls: "x-btn-icon x-grid-loading",
27462             handler: this.onClick.createDelegate(this, ["refresh"])
27463         });
27464
27465         if(this.displayInfo){
27466             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27467         }
27468     },
27469
27470     // private
27471     updateInfo : function(){
27472         if(this.displayEl){
27473             var count = this.ds.getCount();
27474             var msg = count == 0 ?
27475                 this.emptyMsg :
27476                 String.format(
27477                     this.displayMsg,
27478                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27479                 );
27480             this.displayEl.update(msg);
27481         }
27482     },
27483
27484     // private
27485     onLoad : function(ds, r, o){
27486        this.cursor = o.params ? o.params.start : 0;
27487        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27488
27489        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27490        this.field.dom.value = ap;
27491        this.first.setDisabled(ap == 1);
27492        this.prev.setDisabled(ap == 1);
27493        this.next.setDisabled(ap == ps);
27494        this.last.setDisabled(ap == ps);
27495        this.loading.enable();
27496        this.updateInfo();
27497     },
27498
27499     // private
27500     getPageData : function(){
27501         var total = this.ds.getTotalCount();
27502         return {
27503             total : total,
27504             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27505             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27506         };
27507     },
27508
27509     // private
27510     onLoadError : function(){
27511         this.loading.enable();
27512     },
27513
27514     // private
27515     onPagingKeydown : function(e){
27516         var k = e.getKey();
27517         var d = this.getPageData();
27518         if(k == e.RETURN){
27519             var v = this.field.dom.value, pageNum;
27520             if(!v || isNaN(pageNum = parseInt(v, 10))){
27521                 this.field.dom.value = d.activePage;
27522                 return;
27523             }
27524             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27525             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27526             e.stopEvent();
27527         }
27528         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))
27529         {
27530           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27531           this.field.dom.value = pageNum;
27532           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27533           e.stopEvent();
27534         }
27535         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27536         {
27537           var v = this.field.dom.value, pageNum; 
27538           var increment = (e.shiftKey) ? 10 : 1;
27539           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27540             increment *= -1;
27541           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27542             this.field.dom.value = d.activePage;
27543             return;
27544           }
27545           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27546           {
27547             this.field.dom.value = parseInt(v, 10) + increment;
27548             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27549             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27550           }
27551           e.stopEvent();
27552         }
27553     },
27554
27555     // private
27556     beforeLoad : function(){
27557         if(this.loading){
27558             this.loading.disable();
27559         }
27560     },
27561
27562     // private
27563     onClick : function(which){
27564         var ds = this.ds;
27565         switch(which){
27566             case "first":
27567                 ds.load({params:{start: 0, limit: this.pageSize}});
27568             break;
27569             case "prev":
27570                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27571             break;
27572             case "next":
27573                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27574             break;
27575             case "last":
27576                 var total = ds.getTotalCount();
27577                 var extra = total % this.pageSize;
27578                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27579                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27580             break;
27581             case "refresh":
27582                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27583             break;
27584         }
27585     },
27586
27587     /**
27588      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27589      * @param {Roo.data.Store} store The data store to unbind
27590      */
27591     unbind : function(ds){
27592         ds.un("beforeload", this.beforeLoad, this);
27593         ds.un("load", this.onLoad, this);
27594         ds.un("loadexception", this.onLoadError, this);
27595         ds.un("remove", this.updateInfo, this);
27596         ds.un("add", this.updateInfo, this);
27597         this.ds = undefined;
27598     },
27599
27600     /**
27601      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27602      * @param {Roo.data.Store} store The data store to bind
27603      */
27604     bind : function(ds){
27605         ds.on("beforeload", this.beforeLoad, this);
27606         ds.on("load", this.onLoad, this);
27607         ds.on("loadexception", this.onLoadError, this);
27608         ds.on("remove", this.updateInfo, this);
27609         ds.on("add", this.updateInfo, this);
27610         this.ds = ds;
27611     }
27612 });/*
27613  * Based on:
27614  * Ext JS Library 1.1.1
27615  * Copyright(c) 2006-2007, Ext JS, LLC.
27616  *
27617  * Originally Released Under LGPL - original licence link has changed is not relivant.
27618  *
27619  * Fork - LGPL
27620  * <script type="text/javascript">
27621  */
27622
27623 /**
27624  * @class Roo.Resizable
27625  * @extends Roo.util.Observable
27626  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27627  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27628  * 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
27629  * the element will be wrapped for you automatically.</p>
27630  * <p>Here is the list of valid resize handles:</p>
27631  * <pre>
27632 Value   Description
27633 ------  -------------------
27634  'n'     north
27635  's'     south
27636  'e'     east
27637  'w'     west
27638  'nw'    northwest
27639  'sw'    southwest
27640  'se'    southeast
27641  'ne'    northeast
27642  'hd'    horizontal drag
27643  'all'   all
27644 </pre>
27645  * <p>Here's an example showing the creation of a typical Resizable:</p>
27646  * <pre><code>
27647 var resizer = new Roo.Resizable("element-id", {
27648     handles: 'all',
27649     minWidth: 200,
27650     minHeight: 100,
27651     maxWidth: 500,
27652     maxHeight: 400,
27653     pinned: true
27654 });
27655 resizer.on("resize", myHandler);
27656 </code></pre>
27657  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27658  * resizer.east.setDisplayed(false);</p>
27659  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27660  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27661  * resize operation's new size (defaults to [0, 0])
27662  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27663  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27664  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27665  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27666  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27667  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27668  * @cfg {Number} width The width of the element in pixels (defaults to null)
27669  * @cfg {Number} height The height of the element in pixels (defaults to null)
27670  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27671  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27672  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27673  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27674  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27675  * in favor of the handles config option (defaults to false)
27676  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27677  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27678  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27679  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27680  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27681  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27682  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27683  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27684  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27685  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27686  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27687  * @constructor
27688  * Create a new resizable component
27689  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27690  * @param {Object} config configuration options
27691   */
27692 Roo.Resizable = function(el, config)
27693 {
27694     this.el = Roo.get(el);
27695
27696     if(config && config.wrap){
27697         config.resizeChild = this.el;
27698         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27699         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27700         this.el.setStyle("overflow", "hidden");
27701         this.el.setPositioning(config.resizeChild.getPositioning());
27702         config.resizeChild.clearPositioning();
27703         if(!config.width || !config.height){
27704             var csize = config.resizeChild.getSize();
27705             this.el.setSize(csize.width, csize.height);
27706         }
27707         if(config.pinned && !config.adjustments){
27708             config.adjustments = "auto";
27709         }
27710     }
27711
27712     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27713     this.proxy.unselectable();
27714     this.proxy.enableDisplayMode('block');
27715
27716     Roo.apply(this, config);
27717
27718     if(this.pinned){
27719         this.disableTrackOver = true;
27720         this.el.addClass("x-resizable-pinned");
27721     }
27722     // if the element isn't positioned, make it relative
27723     var position = this.el.getStyle("position");
27724     if(position != "absolute" && position != "fixed"){
27725         this.el.setStyle("position", "relative");
27726     }
27727     if(!this.handles){ // no handles passed, must be legacy style
27728         this.handles = 's,e,se';
27729         if(this.multiDirectional){
27730             this.handles += ',n,w';
27731         }
27732     }
27733     if(this.handles == "all"){
27734         this.handles = "n s e w ne nw se sw";
27735     }
27736     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27737     var ps = Roo.Resizable.positions;
27738     for(var i = 0, len = hs.length; i < len; i++){
27739         if(hs[i] && ps[hs[i]]){
27740             var pos = ps[hs[i]];
27741             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27742         }
27743     }
27744     // legacy
27745     this.corner = this.southeast;
27746     
27747     // updateBox = the box can move..
27748     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27749         this.updateBox = true;
27750     }
27751
27752     this.activeHandle = null;
27753
27754     if(this.resizeChild){
27755         if(typeof this.resizeChild == "boolean"){
27756             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27757         }else{
27758             this.resizeChild = Roo.get(this.resizeChild, true);
27759         }
27760     }
27761     
27762     if(this.adjustments == "auto"){
27763         var rc = this.resizeChild;
27764         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27765         if(rc && (hw || hn)){
27766             rc.position("relative");
27767             rc.setLeft(hw ? hw.el.getWidth() : 0);
27768             rc.setTop(hn ? hn.el.getHeight() : 0);
27769         }
27770         this.adjustments = [
27771             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27772             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27773         ];
27774     }
27775
27776     if(this.draggable){
27777         this.dd = this.dynamic ?
27778             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27779         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27780     }
27781
27782     // public events
27783     this.addEvents({
27784         /**
27785          * @event beforeresize
27786          * Fired before resize is allowed. Set enabled to false to cancel resize.
27787          * @param {Roo.Resizable} this
27788          * @param {Roo.EventObject} e The mousedown event
27789          */
27790         "beforeresize" : true,
27791         /**
27792          * @event resize
27793          * Fired after a resize.
27794          * @param {Roo.Resizable} this
27795          * @param {Number} width The new width
27796          * @param {Number} height The new height
27797          * @param {Roo.EventObject} e The mouseup event
27798          */
27799         "resize" : true
27800     });
27801
27802     if(this.width !== null && this.height !== null){
27803         this.resizeTo(this.width, this.height);
27804     }else{
27805         this.updateChildSize();
27806     }
27807     if(Roo.isIE){
27808         this.el.dom.style.zoom = 1;
27809     }
27810     Roo.Resizable.superclass.constructor.call(this);
27811 };
27812
27813 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27814         resizeChild : false,
27815         adjustments : [0, 0],
27816         minWidth : 5,
27817         minHeight : 5,
27818         maxWidth : 10000,
27819         maxHeight : 10000,
27820         enabled : true,
27821         animate : false,
27822         duration : .35,
27823         dynamic : false,
27824         handles : false,
27825         multiDirectional : false,
27826         disableTrackOver : false,
27827         easing : 'easeOutStrong',
27828         widthIncrement : 0,
27829         heightIncrement : 0,
27830         pinned : false,
27831         width : null,
27832         height : null,
27833         preserveRatio : false,
27834         transparent: false,
27835         minX: 0,
27836         minY: 0,
27837         draggable: false,
27838
27839         /**
27840          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27841          */
27842         constrainTo: undefined,
27843         /**
27844          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27845          */
27846         resizeRegion: undefined,
27847
27848
27849     /**
27850      * Perform a manual resize
27851      * @param {Number} width
27852      * @param {Number} height
27853      */
27854     resizeTo : function(width, height){
27855         this.el.setSize(width, height);
27856         this.updateChildSize();
27857         this.fireEvent("resize", this, width, height, null);
27858     },
27859
27860     // private
27861     startSizing : function(e, handle){
27862         this.fireEvent("beforeresize", this, e);
27863         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27864
27865             if(!this.overlay){
27866                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27867                 this.overlay.unselectable();
27868                 this.overlay.enableDisplayMode("block");
27869                 this.overlay.on("mousemove", this.onMouseMove, this);
27870                 this.overlay.on("mouseup", this.onMouseUp, this);
27871             }
27872             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27873
27874             this.resizing = true;
27875             this.startBox = this.el.getBox();
27876             this.startPoint = e.getXY();
27877             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27878                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27879
27880             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27881             this.overlay.show();
27882
27883             if(this.constrainTo) {
27884                 var ct = Roo.get(this.constrainTo);
27885                 this.resizeRegion = ct.getRegion().adjust(
27886                     ct.getFrameWidth('t'),
27887                     ct.getFrameWidth('l'),
27888                     -ct.getFrameWidth('b'),
27889                     -ct.getFrameWidth('r')
27890                 );
27891             }
27892
27893             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27894             this.proxy.show();
27895             this.proxy.setBox(this.startBox);
27896             if(!this.dynamic){
27897                 this.proxy.setStyle('visibility', 'visible');
27898             }
27899         }
27900     },
27901
27902     // private
27903     onMouseDown : function(handle, e){
27904         if(this.enabled){
27905             e.stopEvent();
27906             this.activeHandle = handle;
27907             this.startSizing(e, handle);
27908         }
27909     },
27910
27911     // private
27912     onMouseUp : function(e){
27913         var size = this.resizeElement();
27914         this.resizing = false;
27915         this.handleOut();
27916         this.overlay.hide();
27917         this.proxy.hide();
27918         this.fireEvent("resize", this, size.width, size.height, e);
27919     },
27920
27921     // private
27922     updateChildSize : function(){
27923         if(this.resizeChild){
27924             var el = this.el;
27925             var child = this.resizeChild;
27926             var adj = this.adjustments;
27927             if(el.dom.offsetWidth){
27928                 var b = el.getSize(true);
27929                 child.setSize(b.width+adj[0], b.height+adj[1]);
27930             }
27931             // Second call here for IE
27932             // The first call enables instant resizing and
27933             // the second call corrects scroll bars if they
27934             // exist
27935             if(Roo.isIE){
27936                 setTimeout(function(){
27937                     if(el.dom.offsetWidth){
27938                         var b = el.getSize(true);
27939                         child.setSize(b.width+adj[0], b.height+adj[1]);
27940                     }
27941                 }, 10);
27942             }
27943         }
27944     },
27945
27946     // private
27947     snap : function(value, inc, min){
27948         if(!inc || !value) return value;
27949         var newValue = value;
27950         var m = value % inc;
27951         if(m > 0){
27952             if(m > (inc/2)){
27953                 newValue = value + (inc-m);
27954             }else{
27955                 newValue = value - m;
27956             }
27957         }
27958         return Math.max(min, newValue);
27959     },
27960
27961     // private
27962     resizeElement : function(){
27963         var box = this.proxy.getBox();
27964         if(this.updateBox){
27965             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27966         }else{
27967             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27968         }
27969         this.updateChildSize();
27970         if(!this.dynamic){
27971             this.proxy.hide();
27972         }
27973         return box;
27974     },
27975
27976     // private
27977     constrain : function(v, diff, m, mx){
27978         if(v - diff < m){
27979             diff = v - m;
27980         }else if(v - diff > mx){
27981             diff = mx - v;
27982         }
27983         return diff;
27984     },
27985
27986     // private
27987     onMouseMove : function(e){
27988         if(this.enabled){
27989             try{// try catch so if something goes wrong the user doesn't get hung
27990
27991             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27992                 return;
27993             }
27994
27995             //var curXY = this.startPoint;
27996             var curSize = this.curSize || this.startBox;
27997             var x = this.startBox.x, y = this.startBox.y;
27998             var ox = x, oy = y;
27999             var w = curSize.width, h = curSize.height;
28000             var ow = w, oh = h;
28001             var mw = this.minWidth, mh = this.minHeight;
28002             var mxw = this.maxWidth, mxh = this.maxHeight;
28003             var wi = this.widthIncrement;
28004             var hi = this.heightIncrement;
28005
28006             var eventXY = e.getXY();
28007             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28008             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28009
28010             var pos = this.activeHandle.position;
28011
28012             switch(pos){
28013                 case "east":
28014                     w += diffX;
28015                     w = Math.min(Math.max(mw, w), mxw);
28016                     break;
28017              
28018                 case "south":
28019                     h += diffY;
28020                     h = Math.min(Math.max(mh, h), mxh);
28021                     break;
28022                 case "southeast":
28023                     w += diffX;
28024                     h += diffY;
28025                     w = Math.min(Math.max(mw, w), mxw);
28026                     h = Math.min(Math.max(mh, h), mxh);
28027                     break;
28028                 case "north":
28029                     diffY = this.constrain(h, diffY, mh, mxh);
28030                     y += diffY;
28031                     h -= diffY;
28032                     break;
28033                 case "hdrag":
28034                     
28035                     if (wi) {
28036                         var adiffX = Math.abs(diffX);
28037                         var sub = (adiffX % wi); // how much 
28038                         if (sub > (wi/2)) { // far enough to snap
28039                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28040                         } else {
28041                             // remove difference.. 
28042                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28043                         }
28044                     }
28045                     x += diffX;
28046                     x = Math.max(this.minX, x);
28047                     break;
28048                 case "west":
28049                     diffX = this.constrain(w, diffX, mw, mxw);
28050                     x += diffX;
28051                     w -= diffX;
28052                     break;
28053                 case "northeast":
28054                     w += diffX;
28055                     w = Math.min(Math.max(mw, w), mxw);
28056                     diffY = this.constrain(h, diffY, mh, mxh);
28057                     y += diffY;
28058                     h -= diffY;
28059                     break;
28060                 case "northwest":
28061                     diffX = this.constrain(w, diffX, mw, mxw);
28062                     diffY = this.constrain(h, diffY, mh, mxh);
28063                     y += diffY;
28064                     h -= diffY;
28065                     x += diffX;
28066                     w -= diffX;
28067                     break;
28068                case "southwest":
28069                     diffX = this.constrain(w, diffX, mw, mxw);
28070                     h += diffY;
28071                     h = Math.min(Math.max(mh, h), mxh);
28072                     x += diffX;
28073                     w -= diffX;
28074                     break;
28075             }
28076
28077             var sw = this.snap(w, wi, mw);
28078             var sh = this.snap(h, hi, mh);
28079             if(sw != w || sh != h){
28080                 switch(pos){
28081                     case "northeast":
28082                         y -= sh - h;
28083                     break;
28084                     case "north":
28085                         y -= sh - h;
28086                         break;
28087                     case "southwest":
28088                         x -= sw - w;
28089                     break;
28090                     case "west":
28091                         x -= sw - w;
28092                         break;
28093                     case "northwest":
28094                         x -= sw - w;
28095                         y -= sh - h;
28096                     break;
28097                 }
28098                 w = sw;
28099                 h = sh;
28100             }
28101
28102             if(this.preserveRatio){
28103                 switch(pos){
28104                     case "southeast":
28105                     case "east":
28106                         h = oh * (w/ow);
28107                         h = Math.min(Math.max(mh, h), mxh);
28108                         w = ow * (h/oh);
28109                        break;
28110                     case "south":
28111                         w = ow * (h/oh);
28112                         w = Math.min(Math.max(mw, w), mxw);
28113                         h = oh * (w/ow);
28114                         break;
28115                     case "northeast":
28116                         w = ow * (h/oh);
28117                         w = Math.min(Math.max(mw, w), mxw);
28118                         h = oh * (w/ow);
28119                     break;
28120                     case "north":
28121                         var tw = w;
28122                         w = ow * (h/oh);
28123                         w = Math.min(Math.max(mw, w), mxw);
28124                         h = oh * (w/ow);
28125                         x += (tw - w) / 2;
28126                         break;
28127                     case "southwest":
28128                         h = oh * (w/ow);
28129                         h = Math.min(Math.max(mh, h), mxh);
28130                         var tw = w;
28131                         w = ow * (h/oh);
28132                         x += tw - w;
28133                         break;
28134                     case "west":
28135                         var th = h;
28136                         h = oh * (w/ow);
28137                         h = Math.min(Math.max(mh, h), mxh);
28138                         y += (th - h) / 2;
28139                         var tw = w;
28140                         w = ow * (h/oh);
28141                         x += tw - w;
28142                        break;
28143                     case "northwest":
28144                         var tw = w;
28145                         var th = h;
28146                         h = oh * (w/ow);
28147                         h = Math.min(Math.max(mh, h), mxh);
28148                         w = ow * (h/oh);
28149                         y += th - h;
28150                         x += tw - w;
28151                        break;
28152
28153                 }
28154             }
28155             if (pos == 'hdrag') {
28156                 w = ow;
28157             }
28158             this.proxy.setBounds(x, y, w, h);
28159             if(this.dynamic){
28160                 this.resizeElement();
28161             }
28162             }catch(e){}
28163         }
28164     },
28165
28166     // private
28167     handleOver : function(){
28168         if(this.enabled){
28169             this.el.addClass("x-resizable-over");
28170         }
28171     },
28172
28173     // private
28174     handleOut : function(){
28175         if(!this.resizing){
28176             this.el.removeClass("x-resizable-over");
28177         }
28178     },
28179
28180     /**
28181      * Returns the element this component is bound to.
28182      * @return {Roo.Element}
28183      */
28184     getEl : function(){
28185         return this.el;
28186     },
28187
28188     /**
28189      * Returns the resizeChild element (or null).
28190      * @return {Roo.Element}
28191      */
28192     getResizeChild : function(){
28193         return this.resizeChild;
28194     },
28195
28196     /**
28197      * Destroys this resizable. If the element was wrapped and
28198      * removeEl is not true then the element remains.
28199      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28200      */
28201     destroy : function(removeEl){
28202         this.proxy.remove();
28203         if(this.overlay){
28204             this.overlay.removeAllListeners();
28205             this.overlay.remove();
28206         }
28207         var ps = Roo.Resizable.positions;
28208         for(var k in ps){
28209             if(typeof ps[k] != "function" && this[ps[k]]){
28210                 var h = this[ps[k]];
28211                 h.el.removeAllListeners();
28212                 h.el.remove();
28213             }
28214         }
28215         if(removeEl){
28216             this.el.update("");
28217             this.el.remove();
28218         }
28219     }
28220 });
28221
28222 // private
28223 // hash to map config positions to true positions
28224 Roo.Resizable.positions = {
28225     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28226     hd: "hdrag"
28227 };
28228
28229 // private
28230 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28231     if(!this.tpl){
28232         // only initialize the template if resizable is used
28233         var tpl = Roo.DomHelper.createTemplate(
28234             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28235         );
28236         tpl.compile();
28237         Roo.Resizable.Handle.prototype.tpl = tpl;
28238     }
28239     this.position = pos;
28240     this.rz = rz;
28241     // show north drag fro topdra
28242     var handlepos = pos == 'hdrag' ? 'north' : pos;
28243     
28244     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28245     if (pos == 'hdrag') {
28246         this.el.setStyle('cursor', 'pointer');
28247     }
28248     this.el.unselectable();
28249     if(transparent){
28250         this.el.setOpacity(0);
28251     }
28252     this.el.on("mousedown", this.onMouseDown, this);
28253     if(!disableTrackOver){
28254         this.el.on("mouseover", this.onMouseOver, this);
28255         this.el.on("mouseout", this.onMouseOut, this);
28256     }
28257 };
28258
28259 // private
28260 Roo.Resizable.Handle.prototype = {
28261     afterResize : function(rz){
28262         // do nothing
28263     },
28264     // private
28265     onMouseDown : function(e){
28266         this.rz.onMouseDown(this, e);
28267     },
28268     // private
28269     onMouseOver : function(e){
28270         this.rz.handleOver(this, e);
28271     },
28272     // private
28273     onMouseOut : function(e){
28274         this.rz.handleOut(this, e);
28275     }
28276 };/*
28277  * Based on:
28278  * Ext JS Library 1.1.1
28279  * Copyright(c) 2006-2007, Ext JS, LLC.
28280  *
28281  * Originally Released Under LGPL - original licence link has changed is not relivant.
28282  *
28283  * Fork - LGPL
28284  * <script type="text/javascript">
28285  */
28286
28287 /**
28288  * @class Roo.Editor
28289  * @extends Roo.Component
28290  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28291  * @constructor
28292  * Create a new Editor
28293  * @param {Roo.form.Field} field The Field object (or descendant)
28294  * @param {Object} config The config object
28295  */
28296 Roo.Editor = function(field, config){
28297     Roo.Editor.superclass.constructor.call(this, config);
28298     this.field = field;
28299     this.addEvents({
28300         /**
28301              * @event beforestartedit
28302              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28303              * false from the handler of this event.
28304              * @param {Editor} this
28305              * @param {Roo.Element} boundEl The underlying element bound to this editor
28306              * @param {Mixed} value The field value being set
28307              */
28308         "beforestartedit" : true,
28309         /**
28310              * @event startedit
28311              * Fires when this editor is displayed
28312              * @param {Roo.Element} boundEl The underlying element bound to this editor
28313              * @param {Mixed} value The starting field value
28314              */
28315         "startedit" : true,
28316         /**
28317              * @event beforecomplete
28318              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28319              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28320              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28321              * event will not fire since no edit actually occurred.
28322              * @param {Editor} this
28323              * @param {Mixed} value The current field value
28324              * @param {Mixed} startValue The original field value
28325              */
28326         "beforecomplete" : true,
28327         /**
28328              * @event complete
28329              * Fires after editing is complete and any changed value has been written to the underlying field.
28330              * @param {Editor} this
28331              * @param {Mixed} value The current field value
28332              * @param {Mixed} startValue The original field value
28333              */
28334         "complete" : true,
28335         /**
28336          * @event specialkey
28337          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28338          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28339          * @param {Roo.form.Field} this
28340          * @param {Roo.EventObject} e The event object
28341          */
28342         "specialkey" : true
28343     });
28344 };
28345
28346 Roo.extend(Roo.Editor, Roo.Component, {
28347     /**
28348      * @cfg {Boolean/String} autosize
28349      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28350      * or "height" to adopt the height only (defaults to false)
28351      */
28352     /**
28353      * @cfg {Boolean} revertInvalid
28354      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28355      * validation fails (defaults to true)
28356      */
28357     /**
28358      * @cfg {Boolean} ignoreNoChange
28359      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28360      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28361      * will never be ignored.
28362      */
28363     /**
28364      * @cfg {Boolean} hideEl
28365      * False to keep the bound element visible while the editor is displayed (defaults to true)
28366      */
28367     /**
28368      * @cfg {Mixed} value
28369      * The data value of the underlying field (defaults to "")
28370      */
28371     value : "",
28372     /**
28373      * @cfg {String} alignment
28374      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28375      */
28376     alignment: "c-c?",
28377     /**
28378      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28379      * for bottom-right shadow (defaults to "frame")
28380      */
28381     shadow : "frame",
28382     /**
28383      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28384      */
28385     constrain : false,
28386     /**
28387      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28388      */
28389     completeOnEnter : false,
28390     /**
28391      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28392      */
28393     cancelOnEsc : false,
28394     /**
28395      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28396      */
28397     updateEl : false,
28398
28399     // private
28400     onRender : function(ct, position){
28401         this.el = new Roo.Layer({
28402             shadow: this.shadow,
28403             cls: "x-editor",
28404             parentEl : ct,
28405             shim : this.shim,
28406             shadowOffset:4,
28407             id: this.id,
28408             constrain: this.constrain
28409         });
28410         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28411         if(this.field.msgTarget != 'title'){
28412             this.field.msgTarget = 'qtip';
28413         }
28414         this.field.render(this.el);
28415         if(Roo.isGecko){
28416             this.field.el.dom.setAttribute('autocomplete', 'off');
28417         }
28418         this.field.on("specialkey", this.onSpecialKey, this);
28419         if(this.swallowKeys){
28420             this.field.el.swallowEvent(['keydown','keypress']);
28421         }
28422         this.field.show();
28423         this.field.on("blur", this.onBlur, this);
28424         if(this.field.grow){
28425             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28426         }
28427     },
28428
28429     onSpecialKey : function(field, e)
28430     {
28431         //Roo.log('editor onSpecialKey');
28432         if(this.completeOnEnter && e.getKey() == e.ENTER){
28433             e.stopEvent();
28434             this.completeEdit();
28435             return;
28436         }
28437         // do not fire special key otherwise it might hide close the editor...
28438         if(e.getKey() == e.ENTER){    
28439             return;
28440         }
28441         if(this.cancelOnEsc && e.getKey() == e.ESC){
28442             this.cancelEdit();
28443             return;
28444         } 
28445         this.fireEvent('specialkey', field, e);
28446     
28447     },
28448
28449     /**
28450      * Starts the editing process and shows the editor.
28451      * @param {String/HTMLElement/Element} el The element to edit
28452      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28453       * to the innerHTML of el.
28454      */
28455     startEdit : function(el, value){
28456         if(this.editing){
28457             this.completeEdit();
28458         }
28459         this.boundEl = Roo.get(el);
28460         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28461         if(!this.rendered){
28462             this.render(this.parentEl || document.body);
28463         }
28464         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28465             return;
28466         }
28467         this.startValue = v;
28468         this.field.setValue(v);
28469         if(this.autoSize){
28470             var sz = this.boundEl.getSize();
28471             switch(this.autoSize){
28472                 case "width":
28473                 this.setSize(sz.width,  "");
28474                 break;
28475                 case "height":
28476                 this.setSize("",  sz.height);
28477                 break;
28478                 default:
28479                 this.setSize(sz.width,  sz.height);
28480             }
28481         }
28482         this.el.alignTo(this.boundEl, this.alignment);
28483         this.editing = true;
28484         if(Roo.QuickTips){
28485             Roo.QuickTips.disable();
28486         }
28487         this.show();
28488     },
28489
28490     /**
28491      * Sets the height and width of this editor.
28492      * @param {Number} width The new width
28493      * @param {Number} height The new height
28494      */
28495     setSize : function(w, h){
28496         this.field.setSize(w, h);
28497         if(this.el){
28498             this.el.sync();
28499         }
28500     },
28501
28502     /**
28503      * Realigns the editor to the bound field based on the current alignment config value.
28504      */
28505     realign : function(){
28506         this.el.alignTo(this.boundEl, this.alignment);
28507     },
28508
28509     /**
28510      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28511      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28512      */
28513     completeEdit : function(remainVisible){
28514         if(!this.editing){
28515             return;
28516         }
28517         var v = this.getValue();
28518         if(this.revertInvalid !== false && !this.field.isValid()){
28519             v = this.startValue;
28520             this.cancelEdit(true);
28521         }
28522         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28523             this.editing = false;
28524             this.hide();
28525             return;
28526         }
28527         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28528             this.editing = false;
28529             if(this.updateEl && this.boundEl){
28530                 this.boundEl.update(v);
28531             }
28532             if(remainVisible !== true){
28533                 this.hide();
28534             }
28535             this.fireEvent("complete", this, v, this.startValue);
28536         }
28537     },
28538
28539     // private
28540     onShow : function(){
28541         this.el.show();
28542         if(this.hideEl !== false){
28543             this.boundEl.hide();
28544         }
28545         this.field.show();
28546         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28547             this.fixIEFocus = true;
28548             this.deferredFocus.defer(50, this);
28549         }else{
28550             this.field.focus();
28551         }
28552         this.fireEvent("startedit", this.boundEl, this.startValue);
28553     },
28554
28555     deferredFocus : function(){
28556         if(this.editing){
28557             this.field.focus();
28558         }
28559     },
28560
28561     /**
28562      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28563      * reverted to the original starting value.
28564      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28565      * cancel (defaults to false)
28566      */
28567     cancelEdit : function(remainVisible){
28568         if(this.editing){
28569             this.setValue(this.startValue);
28570             if(remainVisible !== true){
28571                 this.hide();
28572             }
28573         }
28574     },
28575
28576     // private
28577     onBlur : function(){
28578         if(this.allowBlur !== true && this.editing){
28579             this.completeEdit();
28580         }
28581     },
28582
28583     // private
28584     onHide : function(){
28585         if(this.editing){
28586             this.completeEdit();
28587             return;
28588         }
28589         this.field.blur();
28590         if(this.field.collapse){
28591             this.field.collapse();
28592         }
28593         this.el.hide();
28594         if(this.hideEl !== false){
28595             this.boundEl.show();
28596         }
28597         if(Roo.QuickTips){
28598             Roo.QuickTips.enable();
28599         }
28600     },
28601
28602     /**
28603      * Sets the data value of the editor
28604      * @param {Mixed} value Any valid value supported by the underlying field
28605      */
28606     setValue : function(v){
28607         this.field.setValue(v);
28608     },
28609
28610     /**
28611      * Gets the data value of the editor
28612      * @return {Mixed} The data value
28613      */
28614     getValue : function(){
28615         return this.field.getValue();
28616     }
28617 });/*
28618  * Based on:
28619  * Ext JS Library 1.1.1
28620  * Copyright(c) 2006-2007, Ext JS, LLC.
28621  *
28622  * Originally Released Under LGPL - original licence link has changed is not relivant.
28623  *
28624  * Fork - LGPL
28625  * <script type="text/javascript">
28626  */
28627  
28628 /**
28629  * @class Roo.BasicDialog
28630  * @extends Roo.util.Observable
28631  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28632  * <pre><code>
28633 var dlg = new Roo.BasicDialog("my-dlg", {
28634     height: 200,
28635     width: 300,
28636     minHeight: 100,
28637     minWidth: 150,
28638     modal: true,
28639     proxyDrag: true,
28640     shadow: true
28641 });
28642 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28643 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28644 dlg.addButton('Cancel', dlg.hide, dlg);
28645 dlg.show();
28646 </code></pre>
28647   <b>A Dialog should always be a direct child of the body element.</b>
28648  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28649  * @cfg {String} title Default text to display in the title bar (defaults to null)
28650  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28651  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28652  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28653  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28654  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28655  * (defaults to null with no animation)
28656  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28657  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28658  * property for valid values (defaults to 'all')
28659  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28660  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28661  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28662  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28663  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28664  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28665  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28666  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28667  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28668  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28669  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28670  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28671  * draggable = true (defaults to false)
28672  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28673  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28674  * shadow (defaults to false)
28675  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28676  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28677  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28678  * @cfg {Array} buttons Array of buttons
28679  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28680  * @constructor
28681  * Create a new BasicDialog.
28682  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28683  * @param {Object} config Configuration options
28684  */
28685 Roo.BasicDialog = function(el, config){
28686     this.el = Roo.get(el);
28687     var dh = Roo.DomHelper;
28688     if(!this.el && config && config.autoCreate){
28689         if(typeof config.autoCreate == "object"){
28690             if(!config.autoCreate.id){
28691                 config.autoCreate.id = el;
28692             }
28693             this.el = dh.append(document.body,
28694                         config.autoCreate, true);
28695         }else{
28696             this.el = dh.append(document.body,
28697                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28698         }
28699     }
28700     el = this.el;
28701     el.setDisplayed(true);
28702     el.hide = this.hideAction;
28703     this.id = el.id;
28704     el.addClass("x-dlg");
28705
28706     Roo.apply(this, config);
28707
28708     this.proxy = el.createProxy("x-dlg-proxy");
28709     this.proxy.hide = this.hideAction;
28710     this.proxy.setOpacity(.5);
28711     this.proxy.hide();
28712
28713     if(config.width){
28714         el.setWidth(config.width);
28715     }
28716     if(config.height){
28717         el.setHeight(config.height);
28718     }
28719     this.size = el.getSize();
28720     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28721         this.xy = [config.x,config.y];
28722     }else{
28723         this.xy = el.getCenterXY(true);
28724     }
28725     /** The header element @type Roo.Element */
28726     this.header = el.child("> .x-dlg-hd");
28727     /** The body element @type Roo.Element */
28728     this.body = el.child("> .x-dlg-bd");
28729     /** The footer element @type Roo.Element */
28730     this.footer = el.child("> .x-dlg-ft");
28731
28732     if(!this.header){
28733         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28734     }
28735     if(!this.body){
28736         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28737     }
28738
28739     this.header.unselectable();
28740     if(this.title){
28741         this.header.update(this.title);
28742     }
28743     // this element allows the dialog to be focused for keyboard event
28744     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28745     this.focusEl.swallowEvent("click", true);
28746
28747     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28748
28749     // wrap the body and footer for special rendering
28750     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28751     if(this.footer){
28752         this.bwrap.dom.appendChild(this.footer.dom);
28753     }
28754
28755     this.bg = this.el.createChild({
28756         tag: "div", cls:"x-dlg-bg",
28757         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28758     });
28759     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28760
28761
28762     if(this.autoScroll !== false && !this.autoTabs){
28763         this.body.setStyle("overflow", "auto");
28764     }
28765
28766     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28767
28768     if(this.closable !== false){
28769         this.el.addClass("x-dlg-closable");
28770         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28771         this.close.on("click", this.closeClick, this);
28772         this.close.addClassOnOver("x-dlg-close-over");
28773     }
28774     if(this.collapsible !== false){
28775         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28776         this.collapseBtn.on("click", this.collapseClick, this);
28777         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28778         this.header.on("dblclick", this.collapseClick, this);
28779     }
28780     if(this.resizable !== false){
28781         this.el.addClass("x-dlg-resizable");
28782         this.resizer = new Roo.Resizable(el, {
28783             minWidth: this.minWidth || 80,
28784             minHeight:this.minHeight || 80,
28785             handles: this.resizeHandles || "all",
28786             pinned: true
28787         });
28788         this.resizer.on("beforeresize", this.beforeResize, this);
28789         this.resizer.on("resize", this.onResize, this);
28790     }
28791     if(this.draggable !== false){
28792         el.addClass("x-dlg-draggable");
28793         if (!this.proxyDrag) {
28794             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28795         }
28796         else {
28797             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28798         }
28799         dd.setHandleElId(this.header.id);
28800         dd.endDrag = this.endMove.createDelegate(this);
28801         dd.startDrag = this.startMove.createDelegate(this);
28802         dd.onDrag = this.onDrag.createDelegate(this);
28803         dd.scroll = false;
28804         this.dd = dd;
28805     }
28806     if(this.modal){
28807         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28808         this.mask.enableDisplayMode("block");
28809         this.mask.hide();
28810         this.el.addClass("x-dlg-modal");
28811     }
28812     if(this.shadow){
28813         this.shadow = new Roo.Shadow({
28814             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28815             offset : this.shadowOffset
28816         });
28817     }else{
28818         this.shadowOffset = 0;
28819     }
28820     if(Roo.useShims && this.shim !== false){
28821         this.shim = this.el.createShim();
28822         this.shim.hide = this.hideAction;
28823         this.shim.hide();
28824     }else{
28825         this.shim = false;
28826     }
28827     if(this.autoTabs){
28828         this.initTabs();
28829     }
28830     if (this.buttons) { 
28831         var bts= this.buttons;
28832         this.buttons = [];
28833         Roo.each(bts, function(b) {
28834             this.addButton(b);
28835         }, this);
28836     }
28837     
28838     
28839     this.addEvents({
28840         /**
28841          * @event keydown
28842          * Fires when a key is pressed
28843          * @param {Roo.BasicDialog} this
28844          * @param {Roo.EventObject} e
28845          */
28846         "keydown" : true,
28847         /**
28848          * @event move
28849          * Fires when this dialog is moved by the user.
28850          * @param {Roo.BasicDialog} this
28851          * @param {Number} x The new page X
28852          * @param {Number} y The new page Y
28853          */
28854         "move" : true,
28855         /**
28856          * @event resize
28857          * Fires when this dialog is resized by the user.
28858          * @param {Roo.BasicDialog} this
28859          * @param {Number} width The new width
28860          * @param {Number} height The new height
28861          */
28862         "resize" : true,
28863         /**
28864          * @event beforehide
28865          * Fires before this dialog is hidden.
28866          * @param {Roo.BasicDialog} this
28867          */
28868         "beforehide" : true,
28869         /**
28870          * @event hide
28871          * Fires when this dialog is hidden.
28872          * @param {Roo.BasicDialog} this
28873          */
28874         "hide" : true,
28875         /**
28876          * @event beforeshow
28877          * Fires before this dialog is shown.
28878          * @param {Roo.BasicDialog} this
28879          */
28880         "beforeshow" : true,
28881         /**
28882          * @event show
28883          * Fires when this dialog is shown.
28884          * @param {Roo.BasicDialog} this
28885          */
28886         "show" : true
28887     });
28888     el.on("keydown", this.onKeyDown, this);
28889     el.on("mousedown", this.toFront, this);
28890     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28891     this.el.hide();
28892     Roo.DialogManager.register(this);
28893     Roo.BasicDialog.superclass.constructor.call(this);
28894 };
28895
28896 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28897     shadowOffset: Roo.isIE ? 6 : 5,
28898     minHeight: 80,
28899     minWidth: 200,
28900     minButtonWidth: 75,
28901     defaultButton: null,
28902     buttonAlign: "right",
28903     tabTag: 'div',
28904     firstShow: true,
28905
28906     /**
28907      * Sets the dialog title text
28908      * @param {String} text The title text to display
28909      * @return {Roo.BasicDialog} this
28910      */
28911     setTitle : function(text){
28912         this.header.update(text);
28913         return this;
28914     },
28915
28916     // private
28917     closeClick : function(){
28918         this.hide();
28919     },
28920
28921     // private
28922     collapseClick : function(){
28923         this[this.collapsed ? "expand" : "collapse"]();
28924     },
28925
28926     /**
28927      * Collapses the dialog to its minimized state (only the title bar is visible).
28928      * Equivalent to the user clicking the collapse dialog button.
28929      */
28930     collapse : function(){
28931         if(!this.collapsed){
28932             this.collapsed = true;
28933             this.el.addClass("x-dlg-collapsed");
28934             this.restoreHeight = this.el.getHeight();
28935             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28936         }
28937     },
28938
28939     /**
28940      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28941      * clicking the expand dialog button.
28942      */
28943     expand : function(){
28944         if(this.collapsed){
28945             this.collapsed = false;
28946             this.el.removeClass("x-dlg-collapsed");
28947             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28948         }
28949     },
28950
28951     /**
28952      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28953      * @return {Roo.TabPanel} The tabs component
28954      */
28955     initTabs : function(){
28956         var tabs = this.getTabs();
28957         while(tabs.getTab(0)){
28958             tabs.removeTab(0);
28959         }
28960         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28961             var dom = el.dom;
28962             tabs.addTab(Roo.id(dom), dom.title);
28963             dom.title = "";
28964         });
28965         tabs.activate(0);
28966         return tabs;
28967     },
28968
28969     // private
28970     beforeResize : function(){
28971         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28972     },
28973
28974     // private
28975     onResize : function(){
28976         this.refreshSize();
28977         this.syncBodyHeight();
28978         this.adjustAssets();
28979         this.focus();
28980         this.fireEvent("resize", this, this.size.width, this.size.height);
28981     },
28982
28983     // private
28984     onKeyDown : function(e){
28985         if(this.isVisible()){
28986             this.fireEvent("keydown", this, e);
28987         }
28988     },
28989
28990     /**
28991      * Resizes the dialog.
28992      * @param {Number} width
28993      * @param {Number} height
28994      * @return {Roo.BasicDialog} this
28995      */
28996     resizeTo : function(width, height){
28997         this.el.setSize(width, height);
28998         this.size = {width: width, height: height};
28999         this.syncBodyHeight();
29000         if(this.fixedcenter){
29001             this.center();
29002         }
29003         if(this.isVisible()){
29004             this.constrainXY();
29005             this.adjustAssets();
29006         }
29007         this.fireEvent("resize", this, width, height);
29008         return this;
29009     },
29010
29011
29012     /**
29013      * Resizes the dialog to fit the specified content size.
29014      * @param {Number} width
29015      * @param {Number} height
29016      * @return {Roo.BasicDialog} this
29017      */
29018     setContentSize : function(w, h){
29019         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29020         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29021         //if(!this.el.isBorderBox()){
29022             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29023             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29024         //}
29025         if(this.tabs){
29026             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29027             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29028         }
29029         this.resizeTo(w, h);
29030         return this;
29031     },
29032
29033     /**
29034      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29035      * executed in response to a particular key being pressed while the dialog is active.
29036      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29037      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29038      * @param {Function} fn The function to call
29039      * @param {Object} scope (optional) The scope of the function
29040      * @return {Roo.BasicDialog} this
29041      */
29042     addKeyListener : function(key, fn, scope){
29043         var keyCode, shift, ctrl, alt;
29044         if(typeof key == "object" && !(key instanceof Array)){
29045             keyCode = key["key"];
29046             shift = key["shift"];
29047             ctrl = key["ctrl"];
29048             alt = key["alt"];
29049         }else{
29050             keyCode = key;
29051         }
29052         var handler = function(dlg, e){
29053             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29054                 var k = e.getKey();
29055                 if(keyCode instanceof Array){
29056                     for(var i = 0, len = keyCode.length; i < len; i++){
29057                         if(keyCode[i] == k){
29058                           fn.call(scope || window, dlg, k, e);
29059                           return;
29060                         }
29061                     }
29062                 }else{
29063                     if(k == keyCode){
29064                         fn.call(scope || window, dlg, k, e);
29065                     }
29066                 }
29067             }
29068         };
29069         this.on("keydown", handler);
29070         return this;
29071     },
29072
29073     /**
29074      * Returns the TabPanel component (creates it if it doesn't exist).
29075      * Note: If you wish to simply check for the existence of tabs without creating them,
29076      * check for a null 'tabs' property.
29077      * @return {Roo.TabPanel} The tabs component
29078      */
29079     getTabs : function(){
29080         if(!this.tabs){
29081             this.el.addClass("x-dlg-auto-tabs");
29082             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29083             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29084         }
29085         return this.tabs;
29086     },
29087
29088     /**
29089      * Adds a button to the footer section of the dialog.
29090      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29091      * object or a valid Roo.DomHelper element config
29092      * @param {Function} handler The function called when the button is clicked
29093      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29094      * @return {Roo.Button} The new button
29095      */
29096     addButton : function(config, handler, scope){
29097         var dh = Roo.DomHelper;
29098         if(!this.footer){
29099             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29100         }
29101         if(!this.btnContainer){
29102             var tb = this.footer.createChild({
29103
29104                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29105                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29106             }, null, true);
29107             this.btnContainer = tb.firstChild.firstChild.firstChild;
29108         }
29109         var bconfig = {
29110             handler: handler,
29111             scope: scope,
29112             minWidth: this.minButtonWidth,
29113             hideParent:true
29114         };
29115         if(typeof config == "string"){
29116             bconfig.text = config;
29117         }else{
29118             if(config.tag){
29119                 bconfig.dhconfig = config;
29120             }else{
29121                 Roo.apply(bconfig, config);
29122             }
29123         }
29124         var fc = false;
29125         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29126             bconfig.position = Math.max(0, bconfig.position);
29127             fc = this.btnContainer.childNodes[bconfig.position];
29128         }
29129          
29130         var btn = new Roo.Button(
29131             fc ? 
29132                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29133                 : this.btnContainer.appendChild(document.createElement("td")),
29134             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29135             bconfig
29136         );
29137         this.syncBodyHeight();
29138         if(!this.buttons){
29139             /**
29140              * Array of all the buttons that have been added to this dialog via addButton
29141              * @type Array
29142              */
29143             this.buttons = [];
29144         }
29145         this.buttons.push(btn);
29146         return btn;
29147     },
29148
29149     /**
29150      * Sets the default button to be focused when the dialog is displayed.
29151      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29152      * @return {Roo.BasicDialog} this
29153      */
29154     setDefaultButton : function(btn){
29155         this.defaultButton = btn;
29156         return this;
29157     },
29158
29159     // private
29160     getHeaderFooterHeight : function(safe){
29161         var height = 0;
29162         if(this.header){
29163            height += this.header.getHeight();
29164         }
29165         if(this.footer){
29166            var fm = this.footer.getMargins();
29167             height += (this.footer.getHeight()+fm.top+fm.bottom);
29168         }
29169         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29170         height += this.centerBg.getPadding("tb");
29171         return height;
29172     },
29173
29174     // private
29175     syncBodyHeight : function(){
29176         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29177         var height = this.size.height - this.getHeaderFooterHeight(false);
29178         bd.setHeight(height-bd.getMargins("tb"));
29179         var hh = this.header.getHeight();
29180         var h = this.size.height-hh;
29181         cb.setHeight(h);
29182         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29183         bw.setHeight(h-cb.getPadding("tb"));
29184         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29185         bd.setWidth(bw.getWidth(true));
29186         if(this.tabs){
29187             this.tabs.syncHeight();
29188             if(Roo.isIE){
29189                 this.tabs.el.repaint();
29190             }
29191         }
29192     },
29193
29194     /**
29195      * Restores the previous state of the dialog if Roo.state is configured.
29196      * @return {Roo.BasicDialog} this
29197      */
29198     restoreState : function(){
29199         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29200         if(box && box.width){
29201             this.xy = [box.x, box.y];
29202             this.resizeTo(box.width, box.height);
29203         }
29204         return this;
29205     },
29206
29207     // private
29208     beforeShow : function(){
29209         this.expand();
29210         if(this.fixedcenter){
29211             this.xy = this.el.getCenterXY(true);
29212         }
29213         if(this.modal){
29214             Roo.get(document.body).addClass("x-body-masked");
29215             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29216             this.mask.show();
29217         }
29218         this.constrainXY();
29219     },
29220
29221     // private
29222     animShow : function(){
29223         var b = Roo.get(this.animateTarget).getBox();
29224         this.proxy.setSize(b.width, b.height);
29225         this.proxy.setLocation(b.x, b.y);
29226         this.proxy.show();
29227         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29228                     true, .35, this.showEl.createDelegate(this));
29229     },
29230
29231     /**
29232      * Shows the dialog.
29233      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29234      * @return {Roo.BasicDialog} this
29235      */
29236     show : function(animateTarget){
29237         if (this.fireEvent("beforeshow", this) === false){
29238             return;
29239         }
29240         if(this.syncHeightBeforeShow){
29241             this.syncBodyHeight();
29242         }else if(this.firstShow){
29243             this.firstShow = false;
29244             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29245         }
29246         this.animateTarget = animateTarget || this.animateTarget;
29247         if(!this.el.isVisible()){
29248             this.beforeShow();
29249             if(this.animateTarget && Roo.get(this.animateTarget)){
29250                 this.animShow();
29251             }else{
29252                 this.showEl();
29253             }
29254         }
29255         return this;
29256     },
29257
29258     // private
29259     showEl : function(){
29260         this.proxy.hide();
29261         this.el.setXY(this.xy);
29262         this.el.show();
29263         this.adjustAssets(true);
29264         this.toFront();
29265         this.focus();
29266         // IE peekaboo bug - fix found by Dave Fenwick
29267         if(Roo.isIE){
29268             this.el.repaint();
29269         }
29270         this.fireEvent("show", this);
29271     },
29272
29273     /**
29274      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29275      * dialog itself will receive focus.
29276      */
29277     focus : function(){
29278         if(this.defaultButton){
29279             this.defaultButton.focus();
29280         }else{
29281             this.focusEl.focus();
29282         }
29283     },
29284
29285     // private
29286     constrainXY : function(){
29287         if(this.constraintoviewport !== false){
29288             if(!this.viewSize){
29289                 if(this.container){
29290                     var s = this.container.getSize();
29291                     this.viewSize = [s.width, s.height];
29292                 }else{
29293                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29294                 }
29295             }
29296             var s = Roo.get(this.container||document).getScroll();
29297
29298             var x = this.xy[0], y = this.xy[1];
29299             var w = this.size.width, h = this.size.height;
29300             var vw = this.viewSize[0], vh = this.viewSize[1];
29301             // only move it if it needs it
29302             var moved = false;
29303             // first validate right/bottom
29304             if(x + w > vw+s.left){
29305                 x = vw - w;
29306                 moved = true;
29307             }
29308             if(y + h > vh+s.top){
29309                 y = vh - h;
29310                 moved = true;
29311             }
29312             // then make sure top/left isn't negative
29313             if(x < s.left){
29314                 x = s.left;
29315                 moved = true;
29316             }
29317             if(y < s.top){
29318                 y = s.top;
29319                 moved = true;
29320             }
29321             if(moved){
29322                 // cache xy
29323                 this.xy = [x, y];
29324                 if(this.isVisible()){
29325                     this.el.setLocation(x, y);
29326                     this.adjustAssets();
29327                 }
29328             }
29329         }
29330     },
29331
29332     // private
29333     onDrag : function(){
29334         if(!this.proxyDrag){
29335             this.xy = this.el.getXY();
29336             this.adjustAssets();
29337         }
29338     },
29339
29340     // private
29341     adjustAssets : function(doShow){
29342         var x = this.xy[0], y = this.xy[1];
29343         var w = this.size.width, h = this.size.height;
29344         if(doShow === true){
29345             if(this.shadow){
29346                 this.shadow.show(this.el);
29347             }
29348             if(this.shim){
29349                 this.shim.show();
29350             }
29351         }
29352         if(this.shadow && this.shadow.isVisible()){
29353             this.shadow.show(this.el);
29354         }
29355         if(this.shim && this.shim.isVisible()){
29356             this.shim.setBounds(x, y, w, h);
29357         }
29358     },
29359
29360     // private
29361     adjustViewport : function(w, h){
29362         if(!w || !h){
29363             w = Roo.lib.Dom.getViewWidth();
29364             h = Roo.lib.Dom.getViewHeight();
29365         }
29366         // cache the size
29367         this.viewSize = [w, h];
29368         if(this.modal && this.mask.isVisible()){
29369             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29370             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29371         }
29372         if(this.isVisible()){
29373             this.constrainXY();
29374         }
29375     },
29376
29377     /**
29378      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29379      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29380      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29381      */
29382     destroy : function(removeEl){
29383         if(this.isVisible()){
29384             this.animateTarget = null;
29385             this.hide();
29386         }
29387         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29388         if(this.tabs){
29389             this.tabs.destroy(removeEl);
29390         }
29391         Roo.destroy(
29392              this.shim,
29393              this.proxy,
29394              this.resizer,
29395              this.close,
29396              this.mask
29397         );
29398         if(this.dd){
29399             this.dd.unreg();
29400         }
29401         if(this.buttons){
29402            for(var i = 0, len = this.buttons.length; i < len; i++){
29403                this.buttons[i].destroy();
29404            }
29405         }
29406         this.el.removeAllListeners();
29407         if(removeEl === true){
29408             this.el.update("");
29409             this.el.remove();
29410         }
29411         Roo.DialogManager.unregister(this);
29412     },
29413
29414     // private
29415     startMove : function(){
29416         if(this.proxyDrag){
29417             this.proxy.show();
29418         }
29419         if(this.constraintoviewport !== false){
29420             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29421         }
29422     },
29423
29424     // private
29425     endMove : function(){
29426         if(!this.proxyDrag){
29427             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29428         }else{
29429             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29430             this.proxy.hide();
29431         }
29432         this.refreshSize();
29433         this.adjustAssets();
29434         this.focus();
29435         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29436     },
29437
29438     /**
29439      * Brings this dialog to the front of any other visible dialogs
29440      * @return {Roo.BasicDialog} this
29441      */
29442     toFront : function(){
29443         Roo.DialogManager.bringToFront(this);
29444         return this;
29445     },
29446
29447     /**
29448      * Sends this dialog to the back (under) of any other visible dialogs
29449      * @return {Roo.BasicDialog} this
29450      */
29451     toBack : function(){
29452         Roo.DialogManager.sendToBack(this);
29453         return this;
29454     },
29455
29456     /**
29457      * Centers this dialog in the viewport
29458      * @return {Roo.BasicDialog} this
29459      */
29460     center : function(){
29461         var xy = this.el.getCenterXY(true);
29462         this.moveTo(xy[0], xy[1]);
29463         return this;
29464     },
29465
29466     /**
29467      * Moves the dialog's top-left corner to the specified point
29468      * @param {Number} x
29469      * @param {Number} y
29470      * @return {Roo.BasicDialog} this
29471      */
29472     moveTo : function(x, y){
29473         this.xy = [x,y];
29474         if(this.isVisible()){
29475             this.el.setXY(this.xy);
29476             this.adjustAssets();
29477         }
29478         return this;
29479     },
29480
29481     /**
29482      * Aligns the dialog to the specified element
29483      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29484      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29485      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29486      * @return {Roo.BasicDialog} this
29487      */
29488     alignTo : function(element, position, offsets){
29489         this.xy = this.el.getAlignToXY(element, position, offsets);
29490         if(this.isVisible()){
29491             this.el.setXY(this.xy);
29492             this.adjustAssets();
29493         }
29494         return this;
29495     },
29496
29497     /**
29498      * Anchors an element to another element and realigns it when the window is resized.
29499      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29500      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29501      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29502      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29503      * is a number, it is used as the buffer delay (defaults to 50ms).
29504      * @return {Roo.BasicDialog} this
29505      */
29506     anchorTo : function(el, alignment, offsets, monitorScroll){
29507         var action = function(){
29508             this.alignTo(el, alignment, offsets);
29509         };
29510         Roo.EventManager.onWindowResize(action, this);
29511         var tm = typeof monitorScroll;
29512         if(tm != 'undefined'){
29513             Roo.EventManager.on(window, 'scroll', action, this,
29514                 {buffer: tm == 'number' ? monitorScroll : 50});
29515         }
29516         action.call(this);
29517         return this;
29518     },
29519
29520     /**
29521      * Returns true if the dialog is visible
29522      * @return {Boolean}
29523      */
29524     isVisible : function(){
29525         return this.el.isVisible();
29526     },
29527
29528     // private
29529     animHide : function(callback){
29530         var b = Roo.get(this.animateTarget).getBox();
29531         this.proxy.show();
29532         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29533         this.el.hide();
29534         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29535                     this.hideEl.createDelegate(this, [callback]));
29536     },
29537
29538     /**
29539      * Hides the dialog.
29540      * @param {Function} callback (optional) Function to call when the dialog is hidden
29541      * @return {Roo.BasicDialog} this
29542      */
29543     hide : function(callback){
29544         if (this.fireEvent("beforehide", this) === false){
29545             return;
29546         }
29547         if(this.shadow){
29548             this.shadow.hide();
29549         }
29550         if(this.shim) {
29551           this.shim.hide();
29552         }
29553         // sometimes animateTarget seems to get set.. causing problems...
29554         // this just double checks..
29555         if(this.animateTarget && Roo.get(this.animateTarget)) {
29556            this.animHide(callback);
29557         }else{
29558             this.el.hide();
29559             this.hideEl(callback);
29560         }
29561         return this;
29562     },
29563
29564     // private
29565     hideEl : function(callback){
29566         this.proxy.hide();
29567         if(this.modal){
29568             this.mask.hide();
29569             Roo.get(document.body).removeClass("x-body-masked");
29570         }
29571         this.fireEvent("hide", this);
29572         if(typeof callback == "function"){
29573             callback();
29574         }
29575     },
29576
29577     // private
29578     hideAction : function(){
29579         this.setLeft("-10000px");
29580         this.setTop("-10000px");
29581         this.setStyle("visibility", "hidden");
29582     },
29583
29584     // private
29585     refreshSize : function(){
29586         this.size = this.el.getSize();
29587         this.xy = this.el.getXY();
29588         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29589     },
29590
29591     // private
29592     // z-index is managed by the DialogManager and may be overwritten at any time
29593     setZIndex : function(index){
29594         if(this.modal){
29595             this.mask.setStyle("z-index", index);
29596         }
29597         if(this.shim){
29598             this.shim.setStyle("z-index", ++index);
29599         }
29600         if(this.shadow){
29601             this.shadow.setZIndex(++index);
29602         }
29603         this.el.setStyle("z-index", ++index);
29604         if(this.proxy){
29605             this.proxy.setStyle("z-index", ++index);
29606         }
29607         if(this.resizer){
29608             this.resizer.proxy.setStyle("z-index", ++index);
29609         }
29610
29611         this.lastZIndex = index;
29612     },
29613
29614     /**
29615      * Returns the element for this dialog
29616      * @return {Roo.Element} The underlying dialog Element
29617      */
29618     getEl : function(){
29619         return this.el;
29620     }
29621 });
29622
29623 /**
29624  * @class Roo.DialogManager
29625  * Provides global access to BasicDialogs that have been created and
29626  * support for z-indexing (layering) multiple open dialogs.
29627  */
29628 Roo.DialogManager = function(){
29629     var list = {};
29630     var accessList = [];
29631     var front = null;
29632
29633     // private
29634     var sortDialogs = function(d1, d2){
29635         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29636     };
29637
29638     // private
29639     var orderDialogs = function(){
29640         accessList.sort(sortDialogs);
29641         var seed = Roo.DialogManager.zseed;
29642         for(var i = 0, len = accessList.length; i < len; i++){
29643             var dlg = accessList[i];
29644             if(dlg){
29645                 dlg.setZIndex(seed + (i*10));
29646             }
29647         }
29648     };
29649
29650     return {
29651         /**
29652          * The starting z-index for BasicDialogs (defaults to 9000)
29653          * @type Number The z-index value
29654          */
29655         zseed : 9000,
29656
29657         // private
29658         register : function(dlg){
29659             list[dlg.id] = dlg;
29660             accessList.push(dlg);
29661         },
29662
29663         // private
29664         unregister : function(dlg){
29665             delete list[dlg.id];
29666             var i=0;
29667             var len=0;
29668             if(!accessList.indexOf){
29669                 for(  i = 0, len = accessList.length; i < len; i++){
29670                     if(accessList[i] == dlg){
29671                         accessList.splice(i, 1);
29672                         return;
29673                     }
29674                 }
29675             }else{
29676                  i = accessList.indexOf(dlg);
29677                 if(i != -1){
29678                     accessList.splice(i, 1);
29679                 }
29680             }
29681         },
29682
29683         /**
29684          * Gets a registered dialog by id
29685          * @param {String/Object} id The id of the dialog or a dialog
29686          * @return {Roo.BasicDialog} this
29687          */
29688         get : function(id){
29689             return typeof id == "object" ? id : list[id];
29690         },
29691
29692         /**
29693          * Brings the specified dialog to the front
29694          * @param {String/Object} dlg The id of the dialog or a dialog
29695          * @return {Roo.BasicDialog} this
29696          */
29697         bringToFront : function(dlg){
29698             dlg = this.get(dlg);
29699             if(dlg != front){
29700                 front = dlg;
29701                 dlg._lastAccess = new Date().getTime();
29702                 orderDialogs();
29703             }
29704             return dlg;
29705         },
29706
29707         /**
29708          * Sends the specified dialog to the back
29709          * @param {String/Object} dlg The id of the dialog or a dialog
29710          * @return {Roo.BasicDialog} this
29711          */
29712         sendToBack : function(dlg){
29713             dlg = this.get(dlg);
29714             dlg._lastAccess = -(new Date().getTime());
29715             orderDialogs();
29716             return dlg;
29717         },
29718
29719         /**
29720          * Hides all dialogs
29721          */
29722         hideAll : function(){
29723             for(var id in list){
29724                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29725                     list[id].hide();
29726                 }
29727             }
29728         }
29729     };
29730 }();
29731
29732 /**
29733  * @class Roo.LayoutDialog
29734  * @extends Roo.BasicDialog
29735  * Dialog which provides adjustments for working with a layout in a Dialog.
29736  * Add your necessary layout config options to the dialog's config.<br>
29737  * Example usage (including a nested layout):
29738  * <pre><code>
29739 if(!dialog){
29740     dialog = new Roo.LayoutDialog("download-dlg", {
29741         modal: true,
29742         width:600,
29743         height:450,
29744         shadow:true,
29745         minWidth:500,
29746         minHeight:350,
29747         autoTabs:true,
29748         proxyDrag:true,
29749         // layout config merges with the dialog config
29750         center:{
29751             tabPosition: "top",
29752             alwaysShowTabs: true
29753         }
29754     });
29755     dialog.addKeyListener(27, dialog.hide, dialog);
29756     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29757     dialog.addButton("Build It!", this.getDownload, this);
29758
29759     // we can even add nested layouts
29760     var innerLayout = new Roo.BorderLayout("dl-inner", {
29761         east: {
29762             initialSize: 200,
29763             autoScroll:true,
29764             split:true
29765         },
29766         center: {
29767             autoScroll:true
29768         }
29769     });
29770     innerLayout.beginUpdate();
29771     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29772     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29773     innerLayout.endUpdate(true);
29774
29775     var layout = dialog.getLayout();
29776     layout.beginUpdate();
29777     layout.add("center", new Roo.ContentPanel("standard-panel",
29778                         {title: "Download the Source", fitToFrame:true}));
29779     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29780                {title: "Build your own roo.js"}));
29781     layout.getRegion("center").showPanel(sp);
29782     layout.endUpdate();
29783 }
29784 </code></pre>
29785     * @constructor
29786     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29787     * @param {Object} config configuration options
29788   */
29789 Roo.LayoutDialog = function(el, cfg){
29790     
29791     var config=  cfg;
29792     if (typeof(cfg) == 'undefined') {
29793         config = Roo.apply({}, el);
29794         // not sure why we use documentElement here.. - it should always be body.
29795         // IE7 borks horribly if we use documentElement.
29796         // webkit also does not like documentElement - it creates a body element...
29797         el = Roo.get( document.body || document.documentElement ).createChild();
29798         //config.autoCreate = true;
29799     }
29800     
29801     
29802     config.autoTabs = false;
29803     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29804     this.body.setStyle({overflow:"hidden", position:"relative"});
29805     this.layout = new Roo.BorderLayout(this.body.dom, config);
29806     this.layout.monitorWindowResize = false;
29807     this.el.addClass("x-dlg-auto-layout");
29808     // fix case when center region overwrites center function
29809     this.center = Roo.BasicDialog.prototype.center;
29810     this.on("show", this.layout.layout, this.layout, true);
29811     if (config.items) {
29812         var xitems = config.items;
29813         delete config.items;
29814         Roo.each(xitems, this.addxtype, this);
29815     }
29816     
29817     
29818 };
29819 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29820     /**
29821      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29822      * @deprecated
29823      */
29824     endUpdate : function(){
29825         this.layout.endUpdate();
29826     },
29827
29828     /**
29829      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29830      *  @deprecated
29831      */
29832     beginUpdate : function(){
29833         this.layout.beginUpdate();
29834     },
29835
29836     /**
29837      * Get the BorderLayout for this dialog
29838      * @return {Roo.BorderLayout}
29839      */
29840     getLayout : function(){
29841         return this.layout;
29842     },
29843
29844     showEl : function(){
29845         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29846         if(Roo.isIE7){
29847             this.layout.layout();
29848         }
29849     },
29850
29851     // private
29852     // Use the syncHeightBeforeShow config option to control this automatically
29853     syncBodyHeight : function(){
29854         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29855         if(this.layout){this.layout.layout();}
29856     },
29857     
29858       /**
29859      * Add an xtype element (actually adds to the layout.)
29860      * @return {Object} xdata xtype object data.
29861      */
29862     
29863     addxtype : function(c) {
29864         return this.layout.addxtype(c);
29865     }
29866 });/*
29867  * Based on:
29868  * Ext JS Library 1.1.1
29869  * Copyright(c) 2006-2007, Ext JS, LLC.
29870  *
29871  * Originally Released Under LGPL - original licence link has changed is not relivant.
29872  *
29873  * Fork - LGPL
29874  * <script type="text/javascript">
29875  */
29876  
29877 /**
29878  * @class Roo.MessageBox
29879  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29880  * Example usage:
29881  *<pre><code>
29882 // Basic alert:
29883 Roo.Msg.alert('Status', 'Changes saved successfully.');
29884
29885 // Prompt for user data:
29886 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29887     if (btn == 'ok'){
29888         // process text value...
29889     }
29890 });
29891
29892 // Show a dialog using config options:
29893 Roo.Msg.show({
29894    title:'Save Changes?',
29895    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29896    buttons: Roo.Msg.YESNOCANCEL,
29897    fn: processResult,
29898    animEl: 'elId'
29899 });
29900 </code></pre>
29901  * @singleton
29902  */
29903 Roo.MessageBox = function(){
29904     var dlg, opt, mask, waitTimer;
29905     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29906     var buttons, activeTextEl, bwidth;
29907
29908     // private
29909     var handleButton = function(button){
29910         dlg.hide();
29911         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29912     };
29913
29914     // private
29915     var handleHide = function(){
29916         if(opt && opt.cls){
29917             dlg.el.removeClass(opt.cls);
29918         }
29919         if(waitTimer){
29920             Roo.TaskMgr.stop(waitTimer);
29921             waitTimer = null;
29922         }
29923     };
29924
29925     // private
29926     var updateButtons = function(b){
29927         var width = 0;
29928         if(!b){
29929             buttons["ok"].hide();
29930             buttons["cancel"].hide();
29931             buttons["yes"].hide();
29932             buttons["no"].hide();
29933             dlg.footer.dom.style.display = 'none';
29934             return width;
29935         }
29936         dlg.footer.dom.style.display = '';
29937         for(var k in buttons){
29938             if(typeof buttons[k] != "function"){
29939                 if(b[k]){
29940                     buttons[k].show();
29941                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29942                     width += buttons[k].el.getWidth()+15;
29943                 }else{
29944                     buttons[k].hide();
29945                 }
29946             }
29947         }
29948         return width;
29949     };
29950
29951     // private
29952     var handleEsc = function(d, k, e){
29953         if(opt && opt.closable !== false){
29954             dlg.hide();
29955         }
29956         if(e){
29957             e.stopEvent();
29958         }
29959     };
29960
29961     return {
29962         /**
29963          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29964          * @return {Roo.BasicDialog} The BasicDialog element
29965          */
29966         getDialog : function(){
29967            if(!dlg){
29968                 dlg = new Roo.BasicDialog("x-msg-box", {
29969                     autoCreate : true,
29970                     shadow: true,
29971                     draggable: true,
29972                     resizable:false,
29973                     constraintoviewport:false,
29974                     fixedcenter:true,
29975                     collapsible : false,
29976                     shim:true,
29977                     modal: true,
29978                     width:400, height:100,
29979                     buttonAlign:"center",
29980                     closeClick : function(){
29981                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
29982                             handleButton("no");
29983                         }else{
29984                             handleButton("cancel");
29985                         }
29986                     }
29987                 });
29988                 dlg.on("hide", handleHide);
29989                 mask = dlg.mask;
29990                 dlg.addKeyListener(27, handleEsc);
29991                 buttons = {};
29992                 var bt = this.buttonText;
29993                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
29994                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
29995                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
29996                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
29997                 bodyEl = dlg.body.createChild({
29998
29999                     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>'
30000                 });
30001                 msgEl = bodyEl.dom.firstChild;
30002                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30003                 textboxEl.enableDisplayMode();
30004                 textboxEl.addKeyListener([10,13], function(){
30005                     if(dlg.isVisible() && opt && opt.buttons){
30006                         if(opt.buttons.ok){
30007                             handleButton("ok");
30008                         }else if(opt.buttons.yes){
30009                             handleButton("yes");
30010                         }
30011                     }
30012                 });
30013                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30014                 textareaEl.enableDisplayMode();
30015                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30016                 progressEl.enableDisplayMode();
30017                 var pf = progressEl.dom.firstChild;
30018                 if (pf) {
30019                     pp = Roo.get(pf.firstChild);
30020                     pp.setHeight(pf.offsetHeight);
30021                 }
30022                 
30023             }
30024             return dlg;
30025         },
30026
30027         /**
30028          * Updates the message box body text
30029          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30030          * the XHTML-compliant non-breaking space character '&amp;#160;')
30031          * @return {Roo.MessageBox} This message box
30032          */
30033         updateText : function(text){
30034             if(!dlg.isVisible() && !opt.width){
30035                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30036             }
30037             msgEl.innerHTML = text || '&#160;';
30038             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
30039                         Math.max(opt.minWidth || this.minWidth, bwidth));
30040             if(opt.prompt){
30041                 activeTextEl.setWidth(w);
30042             }
30043             if(dlg.isVisible()){
30044                 dlg.fixedcenter = false;
30045             }
30046             dlg.setContentSize(w, bodyEl.getHeight());
30047             if(dlg.isVisible()){
30048                 dlg.fixedcenter = true;
30049             }
30050             return this;
30051         },
30052
30053         /**
30054          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30055          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30056          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30057          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30058          * @return {Roo.MessageBox} This message box
30059          */
30060         updateProgress : function(value, text){
30061             if(text){
30062                 this.updateText(text);
30063             }
30064             if (pp) { // weird bug on my firefox - for some reason this is not defined
30065                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30066             }
30067             return this;
30068         },        
30069
30070         /**
30071          * Returns true if the message box is currently displayed
30072          * @return {Boolean} True if the message box is visible, else false
30073          */
30074         isVisible : function(){
30075             return dlg && dlg.isVisible();  
30076         },
30077
30078         /**
30079          * Hides the message box if it is displayed
30080          */
30081         hide : function(){
30082             if(this.isVisible()){
30083                 dlg.hide();
30084             }  
30085         },
30086
30087         /**
30088          * Displays a new message box, or reinitializes an existing message box, based on the config options
30089          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30090          * The following config object properties are supported:
30091          * <pre>
30092 Property    Type             Description
30093 ----------  ---------------  ------------------------------------------------------------------------------------
30094 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30095                                    closes (defaults to undefined)
30096 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30097                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30098 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30099                                    progress and wait dialogs will ignore this property and always hide the
30100                                    close button as they can only be closed programmatically.
30101 cls               String           A custom CSS class to apply to the message box element
30102 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30103                                    displayed (defaults to 75)
30104 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30105                                    function will be btn (the name of the button that was clicked, if applicable,
30106                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30107                                    Progress and wait dialogs will ignore this option since they do not respond to
30108                                    user actions and can only be closed programmatically, so any required function
30109                                    should be called by the same code after it closes the dialog.
30110 icon              String           A CSS class that provides a background image to be used as an icon for
30111                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30112 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30113 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30114 modal             Boolean          False to allow user interaction with the page while the message box is
30115                                    displayed (defaults to true)
30116 msg               String           A string that will replace the existing message box body text (defaults
30117                                    to the XHTML-compliant non-breaking space character '&#160;')
30118 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30119 progress          Boolean          True to display a progress bar (defaults to false)
30120 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30121 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30122 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30123 title             String           The title text
30124 value             String           The string value to set into the active textbox element if displayed
30125 wait              Boolean          True to display a progress bar (defaults to false)
30126 width             Number           The width of the dialog in pixels
30127 </pre>
30128          *
30129          * Example usage:
30130          * <pre><code>
30131 Roo.Msg.show({
30132    title: 'Address',
30133    msg: 'Please enter your address:',
30134    width: 300,
30135    buttons: Roo.MessageBox.OKCANCEL,
30136    multiline: true,
30137    fn: saveAddress,
30138    animEl: 'addAddressBtn'
30139 });
30140 </code></pre>
30141          * @param {Object} config Configuration options
30142          * @return {Roo.MessageBox} This message box
30143          */
30144         show : function(options){
30145             if(this.isVisible()){
30146                 this.hide();
30147             }
30148             var d = this.getDialog();
30149             opt = options;
30150             d.setTitle(opt.title || "&#160;");
30151             d.close.setDisplayed(opt.closable !== false);
30152             activeTextEl = textboxEl;
30153             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30154             if(opt.prompt){
30155                 if(opt.multiline){
30156                     textboxEl.hide();
30157                     textareaEl.show();
30158                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30159                         opt.multiline : this.defaultTextHeight);
30160                     activeTextEl = textareaEl;
30161                 }else{
30162                     textboxEl.show();
30163                     textareaEl.hide();
30164                 }
30165             }else{
30166                 textboxEl.hide();
30167                 textareaEl.hide();
30168             }
30169             progressEl.setDisplayed(opt.progress === true);
30170             this.updateProgress(0);
30171             activeTextEl.dom.value = opt.value || "";
30172             if(opt.prompt){
30173                 dlg.setDefaultButton(activeTextEl);
30174             }else{
30175                 var bs = opt.buttons;
30176                 var db = null;
30177                 if(bs && bs.ok){
30178                     db = buttons["ok"];
30179                 }else if(bs && bs.yes){
30180                     db = buttons["yes"];
30181                 }
30182                 dlg.setDefaultButton(db);
30183             }
30184             bwidth = updateButtons(opt.buttons);
30185             this.updateText(opt.msg);
30186             if(opt.cls){
30187                 d.el.addClass(opt.cls);
30188             }
30189             d.proxyDrag = opt.proxyDrag === true;
30190             d.modal = opt.modal !== false;
30191             d.mask = opt.modal !== false ? mask : false;
30192             if(!d.isVisible()){
30193                 // force it to the end of the z-index stack so it gets a cursor in FF
30194                 document.body.appendChild(dlg.el.dom);
30195                 d.animateTarget = null;
30196                 d.show(options.animEl);
30197             }
30198             return this;
30199         },
30200
30201         /**
30202          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30203          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30204          * and closing the message box when the process is complete.
30205          * @param {String} title The title bar text
30206          * @param {String} msg The message box body text
30207          * @return {Roo.MessageBox} This message box
30208          */
30209         progress : function(title, msg){
30210             this.show({
30211                 title : title,
30212                 msg : msg,
30213                 buttons: false,
30214                 progress:true,
30215                 closable:false,
30216                 minWidth: this.minProgressWidth,
30217                 modal : true
30218             });
30219             return this;
30220         },
30221
30222         /**
30223          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30224          * If a callback function is passed it will be called after the user clicks the button, and the
30225          * id of the button that was clicked will be passed as the only parameter to the callback
30226          * (could also be the top-right close button).
30227          * @param {String} title The title bar text
30228          * @param {String} msg The message box body text
30229          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30230          * @param {Object} scope (optional) The scope of the callback function
30231          * @return {Roo.MessageBox} This message box
30232          */
30233         alert : function(title, msg, fn, scope){
30234             this.show({
30235                 title : title,
30236                 msg : msg,
30237                 buttons: this.OK,
30238                 fn: fn,
30239                 scope : scope,
30240                 modal : true
30241             });
30242             return this;
30243         },
30244
30245         /**
30246          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30247          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30248          * You are responsible for closing the message box when the process is complete.
30249          * @param {String} msg The message box body text
30250          * @param {String} title (optional) The title bar text
30251          * @return {Roo.MessageBox} This message box
30252          */
30253         wait : function(msg, title){
30254             this.show({
30255                 title : title,
30256                 msg : msg,
30257                 buttons: false,
30258                 closable:false,
30259                 progress:true,
30260                 modal:true,
30261                 width:300,
30262                 wait:true
30263             });
30264             waitTimer = Roo.TaskMgr.start({
30265                 run: function(i){
30266                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30267                 },
30268                 interval: 1000
30269             });
30270             return this;
30271         },
30272
30273         /**
30274          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30275          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30276          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30277          * @param {String} title The title bar text
30278          * @param {String} msg The message box body text
30279          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30280          * @param {Object} scope (optional) The scope of the callback function
30281          * @return {Roo.MessageBox} This message box
30282          */
30283         confirm : function(title, msg, fn, scope){
30284             this.show({
30285                 title : title,
30286                 msg : msg,
30287                 buttons: this.YESNO,
30288                 fn: fn,
30289                 scope : scope,
30290                 modal : true
30291             });
30292             return this;
30293         },
30294
30295         /**
30296          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30297          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30298          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30299          * (could also be the top-right close button) and the text that was entered will be passed as the two
30300          * parameters to the callback.
30301          * @param {String} title The title bar text
30302          * @param {String} msg The message box body text
30303          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30304          * @param {Object} scope (optional) The scope of the callback function
30305          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30306          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30307          * @return {Roo.MessageBox} This message box
30308          */
30309         prompt : function(title, msg, fn, scope, multiline){
30310             this.show({
30311                 title : title,
30312                 msg : msg,
30313                 buttons: this.OKCANCEL,
30314                 fn: fn,
30315                 minWidth:250,
30316                 scope : scope,
30317                 prompt:true,
30318                 multiline: multiline,
30319                 modal : true
30320             });
30321             return this;
30322         },
30323
30324         /**
30325          * Button config that displays a single OK button
30326          * @type Object
30327          */
30328         OK : {ok:true},
30329         /**
30330          * Button config that displays Yes and No buttons
30331          * @type Object
30332          */
30333         YESNO : {yes:true, no:true},
30334         /**
30335          * Button config that displays OK and Cancel buttons
30336          * @type Object
30337          */
30338         OKCANCEL : {ok:true, cancel:true},
30339         /**
30340          * Button config that displays Yes, No and Cancel buttons
30341          * @type Object
30342          */
30343         YESNOCANCEL : {yes:true, no:true, cancel:true},
30344
30345         /**
30346          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30347          * @type Number
30348          */
30349         defaultTextHeight : 75,
30350         /**
30351          * The maximum width in pixels of the message box (defaults to 600)
30352          * @type Number
30353          */
30354         maxWidth : 600,
30355         /**
30356          * The minimum width in pixels of the message box (defaults to 100)
30357          * @type Number
30358          */
30359         minWidth : 100,
30360         /**
30361          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30362          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30363          * @type Number
30364          */
30365         minProgressWidth : 250,
30366         /**
30367          * An object containing the default button text strings that can be overriden for localized language support.
30368          * Supported properties are: ok, cancel, yes and no.
30369          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30370          * @type Object
30371          */
30372         buttonText : {
30373             ok : "OK",
30374             cancel : "Cancel",
30375             yes : "Yes",
30376             no : "No"
30377         }
30378     };
30379 }();
30380
30381 /**
30382  * Shorthand for {@link Roo.MessageBox}
30383  */
30384 Roo.Msg = Roo.MessageBox;/*
30385  * Based on:
30386  * Ext JS Library 1.1.1
30387  * Copyright(c) 2006-2007, Ext JS, LLC.
30388  *
30389  * Originally Released Under LGPL - original licence link has changed is not relivant.
30390  *
30391  * Fork - LGPL
30392  * <script type="text/javascript">
30393  */
30394 /**
30395  * @class Roo.QuickTips
30396  * Provides attractive and customizable tooltips for any element.
30397  * @singleton
30398  */
30399 Roo.QuickTips = function(){
30400     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30401     var ce, bd, xy, dd;
30402     var visible = false, disabled = true, inited = false;
30403     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30404     
30405     var onOver = function(e){
30406         if(disabled){
30407             return;
30408         }
30409         var t = e.getTarget();
30410         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30411             return;
30412         }
30413         if(ce && t == ce.el){
30414             clearTimeout(hideProc);
30415             return;
30416         }
30417         if(t && tagEls[t.id]){
30418             tagEls[t.id].el = t;
30419             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30420             return;
30421         }
30422         var ttp, et = Roo.fly(t);
30423         var ns = cfg.namespace;
30424         if(tm.interceptTitles && t.title){
30425             ttp = t.title;
30426             t.qtip = ttp;
30427             t.removeAttribute("title");
30428             e.preventDefault();
30429         }else{
30430             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30431         }
30432         if(ttp){
30433             showProc = show.defer(tm.showDelay, tm, [{
30434                 el: t, 
30435                 text: ttp, 
30436                 width: et.getAttributeNS(ns, cfg.width),
30437                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30438                 title: et.getAttributeNS(ns, cfg.title),
30439                     cls: et.getAttributeNS(ns, cfg.cls)
30440             }]);
30441         }
30442     };
30443     
30444     var onOut = function(e){
30445         clearTimeout(showProc);
30446         var t = e.getTarget();
30447         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30448             hideProc = setTimeout(hide, tm.hideDelay);
30449         }
30450     };
30451     
30452     var onMove = function(e){
30453         if(disabled){
30454             return;
30455         }
30456         xy = e.getXY();
30457         xy[1] += 18;
30458         if(tm.trackMouse && ce){
30459             el.setXY(xy);
30460         }
30461     };
30462     
30463     var onDown = function(e){
30464         clearTimeout(showProc);
30465         clearTimeout(hideProc);
30466         if(!e.within(el)){
30467             if(tm.hideOnClick){
30468                 hide();
30469                 tm.disable();
30470                 tm.enable.defer(100, tm);
30471             }
30472         }
30473     };
30474     
30475     var getPad = function(){
30476         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30477     };
30478
30479     var show = function(o){
30480         if(disabled){
30481             return;
30482         }
30483         clearTimeout(dismissProc);
30484         ce = o;
30485         if(removeCls){ // in case manually hidden
30486             el.removeClass(removeCls);
30487             removeCls = null;
30488         }
30489         if(ce.cls){
30490             el.addClass(ce.cls);
30491             removeCls = ce.cls;
30492         }
30493         if(ce.title){
30494             tipTitle.update(ce.title);
30495             tipTitle.show();
30496         }else{
30497             tipTitle.update('');
30498             tipTitle.hide();
30499         }
30500         el.dom.style.width  = tm.maxWidth+'px';
30501         //tipBody.dom.style.width = '';
30502         tipBodyText.update(o.text);
30503         var p = getPad(), w = ce.width;
30504         if(!w){
30505             var td = tipBodyText.dom;
30506             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30507             if(aw > tm.maxWidth){
30508                 w = tm.maxWidth;
30509             }else if(aw < tm.minWidth){
30510                 w = tm.minWidth;
30511             }else{
30512                 w = aw;
30513             }
30514         }
30515         //tipBody.setWidth(w);
30516         el.setWidth(parseInt(w, 10) + p);
30517         if(ce.autoHide === false){
30518             close.setDisplayed(true);
30519             if(dd){
30520                 dd.unlock();
30521             }
30522         }else{
30523             close.setDisplayed(false);
30524             if(dd){
30525                 dd.lock();
30526             }
30527         }
30528         if(xy){
30529             el.avoidY = xy[1]-18;
30530             el.setXY(xy);
30531         }
30532         if(tm.animate){
30533             el.setOpacity(.1);
30534             el.setStyle("visibility", "visible");
30535             el.fadeIn({callback: afterShow});
30536         }else{
30537             afterShow();
30538         }
30539     };
30540     
30541     var afterShow = function(){
30542         if(ce){
30543             el.show();
30544             esc.enable();
30545             if(tm.autoDismiss && ce.autoHide !== false){
30546                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30547             }
30548         }
30549     };
30550     
30551     var hide = function(noanim){
30552         clearTimeout(dismissProc);
30553         clearTimeout(hideProc);
30554         ce = null;
30555         if(el.isVisible()){
30556             esc.disable();
30557             if(noanim !== true && tm.animate){
30558                 el.fadeOut({callback: afterHide});
30559             }else{
30560                 afterHide();
30561             } 
30562         }
30563     };
30564     
30565     var afterHide = function(){
30566         el.hide();
30567         if(removeCls){
30568             el.removeClass(removeCls);
30569             removeCls = null;
30570         }
30571     };
30572     
30573     return {
30574         /**
30575         * @cfg {Number} minWidth
30576         * The minimum width of the quick tip (defaults to 40)
30577         */
30578        minWidth : 40,
30579         /**
30580         * @cfg {Number} maxWidth
30581         * The maximum width of the quick tip (defaults to 300)
30582         */
30583        maxWidth : 300,
30584         /**
30585         * @cfg {Boolean} interceptTitles
30586         * True to automatically use the element's DOM title value if available (defaults to false)
30587         */
30588        interceptTitles : false,
30589         /**
30590         * @cfg {Boolean} trackMouse
30591         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30592         */
30593        trackMouse : false,
30594         /**
30595         * @cfg {Boolean} hideOnClick
30596         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30597         */
30598        hideOnClick : true,
30599         /**
30600         * @cfg {Number} showDelay
30601         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30602         */
30603        showDelay : 500,
30604         /**
30605         * @cfg {Number} hideDelay
30606         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30607         */
30608        hideDelay : 200,
30609         /**
30610         * @cfg {Boolean} autoHide
30611         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30612         * Used in conjunction with hideDelay.
30613         */
30614        autoHide : true,
30615         /**
30616         * @cfg {Boolean}
30617         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30618         * (defaults to true).  Used in conjunction with autoDismissDelay.
30619         */
30620        autoDismiss : true,
30621         /**
30622         * @cfg {Number}
30623         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30624         */
30625        autoDismissDelay : 5000,
30626        /**
30627         * @cfg {Boolean} animate
30628         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30629         */
30630        animate : false,
30631
30632        /**
30633         * @cfg {String} title
30634         * Title text to display (defaults to '').  This can be any valid HTML markup.
30635         */
30636         title: '',
30637        /**
30638         * @cfg {String} text
30639         * Body text to display (defaults to '').  This can be any valid HTML markup.
30640         */
30641         text : '',
30642        /**
30643         * @cfg {String} cls
30644         * A CSS class to apply to the base quick tip element (defaults to '').
30645         */
30646         cls : '',
30647        /**
30648         * @cfg {Number} width
30649         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30650         * minWidth or maxWidth.
30651         */
30652         width : null,
30653
30654     /**
30655      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30656      * or display QuickTips in a page.
30657      */
30658        init : function(){
30659           tm = Roo.QuickTips;
30660           cfg = tm.tagConfig;
30661           if(!inited){
30662               if(!Roo.isReady){ // allow calling of init() before onReady
30663                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30664                   return;
30665               }
30666               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30667               el.fxDefaults = {stopFx: true};
30668               // maximum custom styling
30669               //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>');
30670               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>');              
30671               tipTitle = el.child('h3');
30672               tipTitle.enableDisplayMode("block");
30673               tipBody = el.child('div.x-tip-bd');
30674               tipBodyText = el.child('div.x-tip-bd-inner');
30675               //bdLeft = el.child('div.x-tip-bd-left');
30676               //bdRight = el.child('div.x-tip-bd-right');
30677               close = el.child('div.x-tip-close');
30678               close.enableDisplayMode("block");
30679               close.on("click", hide);
30680               var d = Roo.get(document);
30681               d.on("mousedown", onDown);
30682               d.on("mouseover", onOver);
30683               d.on("mouseout", onOut);
30684               d.on("mousemove", onMove);
30685               esc = d.addKeyListener(27, hide);
30686               esc.disable();
30687               if(Roo.dd.DD){
30688                   dd = el.initDD("default", null, {
30689                       onDrag : function(){
30690                           el.sync();  
30691                       }
30692                   });
30693                   dd.setHandleElId(tipTitle.id);
30694                   dd.lock();
30695               }
30696               inited = true;
30697           }
30698           this.enable(); 
30699        },
30700
30701     /**
30702      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30703      * are supported:
30704      * <pre>
30705 Property    Type                   Description
30706 ----------  ---------------------  ------------------------------------------------------------------------
30707 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30708      * </ul>
30709      * @param {Object} config The config object
30710      */
30711        register : function(config){
30712            var cs = config instanceof Array ? config : arguments;
30713            for(var i = 0, len = cs.length; i < len; i++) {
30714                var c = cs[i];
30715                var target = c.target;
30716                if(target){
30717                    if(target instanceof Array){
30718                        for(var j = 0, jlen = target.length; j < jlen; j++){
30719                            tagEls[target[j]] = c;
30720                        }
30721                    }else{
30722                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30723                    }
30724                }
30725            }
30726        },
30727
30728     /**
30729      * Removes this quick tip from its element and destroys it.
30730      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30731      */
30732        unregister : function(el){
30733            delete tagEls[Roo.id(el)];
30734        },
30735
30736     /**
30737      * Enable this quick tip.
30738      */
30739        enable : function(){
30740            if(inited && disabled){
30741                locks.pop();
30742                if(locks.length < 1){
30743                    disabled = false;
30744                }
30745            }
30746        },
30747
30748     /**
30749      * Disable this quick tip.
30750      */
30751        disable : function(){
30752           disabled = true;
30753           clearTimeout(showProc);
30754           clearTimeout(hideProc);
30755           clearTimeout(dismissProc);
30756           if(ce){
30757               hide(true);
30758           }
30759           locks.push(1);
30760        },
30761
30762     /**
30763      * Returns true if the quick tip is enabled, else false.
30764      */
30765        isEnabled : function(){
30766             return !disabled;
30767        },
30768
30769         // private
30770        tagConfig : {
30771            namespace : "ext",
30772            attribute : "qtip",
30773            width : "width",
30774            target : "target",
30775            title : "qtitle",
30776            hide : "hide",
30777            cls : "qclass"
30778        }
30779    };
30780 }();
30781
30782 // backwards compat
30783 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30784  * Based on:
30785  * Ext JS Library 1.1.1
30786  * Copyright(c) 2006-2007, Ext JS, LLC.
30787  *
30788  * Originally Released Under LGPL - original licence link has changed is not relivant.
30789  *
30790  * Fork - LGPL
30791  * <script type="text/javascript">
30792  */
30793  
30794
30795 /**
30796  * @class Roo.tree.TreePanel
30797  * @extends Roo.data.Tree
30798
30799  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30800  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30801  * @cfg {Boolean} enableDD true to enable drag and drop
30802  * @cfg {Boolean} enableDrag true to enable just drag
30803  * @cfg {Boolean} enableDrop true to enable just drop
30804  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30805  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30806  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30807  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30808  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30809  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30810  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30811  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30812  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30813  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30814  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30815  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30816  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30817  * @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>
30818  * @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>
30819  * 
30820  * @constructor
30821  * @param {String/HTMLElement/Element} el The container element
30822  * @param {Object} config
30823  */
30824 Roo.tree.TreePanel = function(el, config){
30825     var root = false;
30826     var loader = false;
30827     if (config.root) {
30828         root = config.root;
30829         delete config.root;
30830     }
30831     if (config.loader) {
30832         loader = config.loader;
30833         delete config.loader;
30834     }
30835     
30836     Roo.apply(this, config);
30837     Roo.tree.TreePanel.superclass.constructor.call(this);
30838     this.el = Roo.get(el);
30839     this.el.addClass('x-tree');
30840     //console.log(root);
30841     if (root) {
30842         this.setRootNode( Roo.factory(root, Roo.tree));
30843     }
30844     if (loader) {
30845         this.loader = Roo.factory(loader, Roo.tree);
30846     }
30847    /**
30848     * Read-only. The id of the container element becomes this TreePanel's id.
30849     */
30850    this.id = this.el.id;
30851    this.addEvents({
30852         /**
30853         * @event beforeload
30854         * Fires before a node is loaded, return false to cancel
30855         * @param {Node} node The node being loaded
30856         */
30857         "beforeload" : true,
30858         /**
30859         * @event load
30860         * Fires when a node is loaded
30861         * @param {Node} node The node that was loaded
30862         */
30863         "load" : true,
30864         /**
30865         * @event textchange
30866         * Fires when the text for a node is changed
30867         * @param {Node} node The node
30868         * @param {String} text The new text
30869         * @param {String} oldText The old text
30870         */
30871         "textchange" : true,
30872         /**
30873         * @event beforeexpand
30874         * Fires before a node is expanded, return false to cancel.
30875         * @param {Node} node The node
30876         * @param {Boolean} deep
30877         * @param {Boolean} anim
30878         */
30879         "beforeexpand" : true,
30880         /**
30881         * @event beforecollapse
30882         * Fires before a node is collapsed, return false to cancel.
30883         * @param {Node} node The node
30884         * @param {Boolean} deep
30885         * @param {Boolean} anim
30886         */
30887         "beforecollapse" : true,
30888         /**
30889         * @event expand
30890         * Fires when a node is expanded
30891         * @param {Node} node The node
30892         */
30893         "expand" : true,
30894         /**
30895         * @event disabledchange
30896         * Fires when the disabled status of a node changes
30897         * @param {Node} node The node
30898         * @param {Boolean} disabled
30899         */
30900         "disabledchange" : true,
30901         /**
30902         * @event collapse
30903         * Fires when a node is collapsed
30904         * @param {Node} node The node
30905         */
30906         "collapse" : true,
30907         /**
30908         * @event beforeclick
30909         * Fires before click processing on a node. Return false to cancel the default action.
30910         * @param {Node} node The node
30911         * @param {Roo.EventObject} e The event object
30912         */
30913         "beforeclick":true,
30914         /**
30915         * @event checkchange
30916         * Fires when a node with a checkbox's checked property changes
30917         * @param {Node} this This node
30918         * @param {Boolean} checked
30919         */
30920         "checkchange":true,
30921         /**
30922         * @event click
30923         * Fires when a node is clicked
30924         * @param {Node} node The node
30925         * @param {Roo.EventObject} e The event object
30926         */
30927         "click":true,
30928         /**
30929         * @event dblclick
30930         * Fires when a node is double clicked
30931         * @param {Node} node The node
30932         * @param {Roo.EventObject} e The event object
30933         */
30934         "dblclick":true,
30935         /**
30936         * @event contextmenu
30937         * Fires when a node is right clicked
30938         * @param {Node} node The node
30939         * @param {Roo.EventObject} e The event object
30940         */
30941         "contextmenu":true,
30942         /**
30943         * @event beforechildrenrendered
30944         * Fires right before the child nodes for a node are rendered
30945         * @param {Node} node The node
30946         */
30947         "beforechildrenrendered":true,
30948        /**
30949              * @event startdrag
30950              * Fires when a node starts being dragged
30951              * @param {Roo.tree.TreePanel} this
30952              * @param {Roo.tree.TreeNode} node
30953              * @param {event} e The raw browser event
30954              */ 
30955             "startdrag" : true,
30956             /**
30957              * @event enddrag
30958              * Fires when a drag operation is complete
30959              * @param {Roo.tree.TreePanel} this
30960              * @param {Roo.tree.TreeNode} node
30961              * @param {event} e The raw browser event
30962              */
30963             "enddrag" : true,
30964             /**
30965              * @event dragdrop
30966              * Fires when a dragged node is dropped on a valid DD target
30967              * @param {Roo.tree.TreePanel} this
30968              * @param {Roo.tree.TreeNode} node
30969              * @param {DD} dd The dd it was dropped on
30970              * @param {event} e The raw browser event
30971              */
30972             "dragdrop" : true,
30973             /**
30974              * @event beforenodedrop
30975              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
30976              * passed to handlers has the following properties:<br />
30977              * <ul style="padding:5px;padding-left:16px;">
30978              * <li>tree - The TreePanel</li>
30979              * <li>target - The node being targeted for the drop</li>
30980              * <li>data - The drag data from the drag source</li>
30981              * <li>point - The point of the drop - append, above or below</li>
30982              * <li>source - The drag source</li>
30983              * <li>rawEvent - Raw mouse event</li>
30984              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
30985              * to be inserted by setting them on this object.</li>
30986              * <li>cancel - Set this to true to cancel the drop.</li>
30987              * </ul>
30988              * @param {Object} dropEvent
30989              */
30990             "beforenodedrop" : true,
30991             /**
30992              * @event nodedrop
30993              * Fires after a DD object is dropped on a node in this tree. The dropEvent
30994              * passed to handlers has the following properties:<br />
30995              * <ul style="padding:5px;padding-left:16px;">
30996              * <li>tree - The TreePanel</li>
30997              * <li>target - The node being targeted for the drop</li>
30998              * <li>data - The drag data from the drag source</li>
30999              * <li>point - The point of the drop - append, above or below</li>
31000              * <li>source - The drag source</li>
31001              * <li>rawEvent - Raw mouse event</li>
31002              * <li>dropNode - Dropped node(s).</li>
31003              * </ul>
31004              * @param {Object} dropEvent
31005              */
31006             "nodedrop" : true,
31007              /**
31008              * @event nodedragover
31009              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31010              * passed to handlers has the following properties:<br />
31011              * <ul style="padding:5px;padding-left:16px;">
31012              * <li>tree - The TreePanel</li>
31013              * <li>target - The node being targeted for the drop</li>
31014              * <li>data - The drag data from the drag source</li>
31015              * <li>point - The point of the drop - append, above or below</li>
31016              * <li>source - The drag source</li>
31017              * <li>rawEvent - Raw mouse event</li>
31018              * <li>dropNode - Drop node(s) provided by the source.</li>
31019              * <li>cancel - Set this to true to signal drop not allowed.</li>
31020              * </ul>
31021              * @param {Object} dragOverEvent
31022              */
31023             "nodedragover" : true
31024         
31025    });
31026    if(this.singleExpand){
31027        this.on("beforeexpand", this.restrictExpand, this);
31028    }
31029 };
31030 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31031     rootVisible : true,
31032     animate: Roo.enableFx,
31033     lines : true,
31034     enableDD : false,
31035     hlDrop : Roo.enableFx,
31036   
31037     renderer: false,
31038     
31039     rendererTip: false,
31040     // private
31041     restrictExpand : function(node){
31042         var p = node.parentNode;
31043         if(p){
31044             if(p.expandedChild && p.expandedChild.parentNode == p){
31045                 p.expandedChild.collapse();
31046             }
31047             p.expandedChild = node;
31048         }
31049     },
31050
31051     // private override
31052     setRootNode : function(node){
31053         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31054         if(!this.rootVisible){
31055             node.ui = new Roo.tree.RootTreeNodeUI(node);
31056         }
31057         return node;
31058     },
31059
31060     /**
31061      * Returns the container element for this TreePanel
31062      */
31063     getEl : function(){
31064         return this.el;
31065     },
31066
31067     /**
31068      * Returns the default TreeLoader for this TreePanel
31069      */
31070     getLoader : function(){
31071         return this.loader;
31072     },
31073
31074     /**
31075      * Expand all nodes
31076      */
31077     expandAll : function(){
31078         this.root.expand(true);
31079     },
31080
31081     /**
31082      * Collapse all nodes
31083      */
31084     collapseAll : function(){
31085         this.root.collapse(true);
31086     },
31087
31088     /**
31089      * Returns the selection model used by this TreePanel
31090      */
31091     getSelectionModel : function(){
31092         if(!this.selModel){
31093             this.selModel = new Roo.tree.DefaultSelectionModel();
31094         }
31095         return this.selModel;
31096     },
31097
31098     /**
31099      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31100      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31101      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31102      * @return {Array}
31103      */
31104     getChecked : function(a, startNode){
31105         startNode = startNode || this.root;
31106         var r = [];
31107         var f = function(){
31108             if(this.attributes.checked){
31109                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31110             }
31111         }
31112         startNode.cascade(f);
31113         return r;
31114     },
31115
31116     /**
31117      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31118      * @param {String} path
31119      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31120      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31121      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31122      */
31123     expandPath : function(path, attr, callback){
31124         attr = attr || "id";
31125         var keys = path.split(this.pathSeparator);
31126         var curNode = this.root;
31127         if(curNode.attributes[attr] != keys[1]){ // invalid root
31128             if(callback){
31129                 callback(false, null);
31130             }
31131             return;
31132         }
31133         var index = 1;
31134         var f = function(){
31135             if(++index == keys.length){
31136                 if(callback){
31137                     callback(true, curNode);
31138                 }
31139                 return;
31140             }
31141             var c = curNode.findChild(attr, keys[index]);
31142             if(!c){
31143                 if(callback){
31144                     callback(false, curNode);
31145                 }
31146                 return;
31147             }
31148             curNode = c;
31149             c.expand(false, false, f);
31150         };
31151         curNode.expand(false, false, f);
31152     },
31153
31154     /**
31155      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31156      * @param {String} path
31157      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31158      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31159      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31160      */
31161     selectPath : function(path, attr, callback){
31162         attr = attr || "id";
31163         var keys = path.split(this.pathSeparator);
31164         var v = keys.pop();
31165         if(keys.length > 0){
31166             var f = function(success, node){
31167                 if(success && node){
31168                     var n = node.findChild(attr, v);
31169                     if(n){
31170                         n.select();
31171                         if(callback){
31172                             callback(true, n);
31173                         }
31174                     }else if(callback){
31175                         callback(false, n);
31176                     }
31177                 }else{
31178                     if(callback){
31179                         callback(false, n);
31180                     }
31181                 }
31182             };
31183             this.expandPath(keys.join(this.pathSeparator), attr, f);
31184         }else{
31185             this.root.select();
31186             if(callback){
31187                 callback(true, this.root);
31188             }
31189         }
31190     },
31191
31192     getTreeEl : function(){
31193         return this.el;
31194     },
31195
31196     /**
31197      * Trigger rendering of this TreePanel
31198      */
31199     render : function(){
31200         if (this.innerCt) {
31201             return this; // stop it rendering more than once!!
31202         }
31203         
31204         this.innerCt = this.el.createChild({tag:"ul",
31205                cls:"x-tree-root-ct " +
31206                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31207
31208         if(this.containerScroll){
31209             Roo.dd.ScrollManager.register(this.el);
31210         }
31211         if((this.enableDD || this.enableDrop) && !this.dropZone){
31212            /**
31213             * The dropZone used by this tree if drop is enabled
31214             * @type Roo.tree.TreeDropZone
31215             */
31216              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31217                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31218            });
31219         }
31220         if((this.enableDD || this.enableDrag) && !this.dragZone){
31221            /**
31222             * The dragZone used by this tree if drag is enabled
31223             * @type Roo.tree.TreeDragZone
31224             */
31225             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31226                ddGroup: this.ddGroup || "TreeDD",
31227                scroll: this.ddScroll
31228            });
31229         }
31230         this.getSelectionModel().init(this);
31231         if (!this.root) {
31232             console.log("ROOT not set in tree");
31233             return;
31234         }
31235         this.root.render();
31236         if(!this.rootVisible){
31237             this.root.renderChildren();
31238         }
31239         return this;
31240     }
31241 });/*
31242  * Based on:
31243  * Ext JS Library 1.1.1
31244  * Copyright(c) 2006-2007, Ext JS, LLC.
31245  *
31246  * Originally Released Under LGPL - original licence link has changed is not relivant.
31247  *
31248  * Fork - LGPL
31249  * <script type="text/javascript">
31250  */
31251  
31252
31253 /**
31254  * @class Roo.tree.DefaultSelectionModel
31255  * @extends Roo.util.Observable
31256  * The default single selection for a TreePanel.
31257  */
31258 Roo.tree.DefaultSelectionModel = function(){
31259    this.selNode = null;
31260    
31261    this.addEvents({
31262        /**
31263         * @event selectionchange
31264         * Fires when the selected node changes
31265         * @param {DefaultSelectionModel} this
31266         * @param {TreeNode} node the new selection
31267         */
31268        "selectionchange" : true,
31269
31270        /**
31271         * @event beforeselect
31272         * Fires before the selected node changes, return false to cancel the change
31273         * @param {DefaultSelectionModel} this
31274         * @param {TreeNode} node the new selection
31275         * @param {TreeNode} node the old selection
31276         */
31277        "beforeselect" : true
31278    });
31279 };
31280
31281 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31282     init : function(tree){
31283         this.tree = tree;
31284         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31285         tree.on("click", this.onNodeClick, this);
31286     },
31287     
31288     onNodeClick : function(node, e){
31289         if (e.ctrlKey && this.selNode == node)  {
31290             this.unselect(node);
31291             return;
31292         }
31293         this.select(node);
31294     },
31295     
31296     /**
31297      * Select a node.
31298      * @param {TreeNode} node The node to select
31299      * @return {TreeNode} The selected node
31300      */
31301     select : function(node){
31302         var last = this.selNode;
31303         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31304             if(last){
31305                 last.ui.onSelectedChange(false);
31306             }
31307             this.selNode = node;
31308             node.ui.onSelectedChange(true);
31309             this.fireEvent("selectionchange", this, node, last);
31310         }
31311         return node;
31312     },
31313     
31314     /**
31315      * Deselect a node.
31316      * @param {TreeNode} node The node to unselect
31317      */
31318     unselect : function(node){
31319         if(this.selNode == node){
31320             this.clearSelections();
31321         }    
31322     },
31323     
31324     /**
31325      * Clear all selections
31326      */
31327     clearSelections : function(){
31328         var n = this.selNode;
31329         if(n){
31330             n.ui.onSelectedChange(false);
31331             this.selNode = null;
31332             this.fireEvent("selectionchange", this, null);
31333         }
31334         return n;
31335     },
31336     
31337     /**
31338      * Get the selected node
31339      * @return {TreeNode} The selected node
31340      */
31341     getSelectedNode : function(){
31342         return this.selNode;    
31343     },
31344     
31345     /**
31346      * Returns true if the node is selected
31347      * @param {TreeNode} node The node to check
31348      * @return {Boolean}
31349      */
31350     isSelected : function(node){
31351         return this.selNode == node;  
31352     },
31353
31354     /**
31355      * Selects the node above the selected node in the tree, intelligently walking the nodes
31356      * @return TreeNode The new selection
31357      */
31358     selectPrevious : function(){
31359         var s = this.selNode || this.lastSelNode;
31360         if(!s){
31361             return null;
31362         }
31363         var ps = s.previousSibling;
31364         if(ps){
31365             if(!ps.isExpanded() || ps.childNodes.length < 1){
31366                 return this.select(ps);
31367             } else{
31368                 var lc = ps.lastChild;
31369                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31370                     lc = lc.lastChild;
31371                 }
31372                 return this.select(lc);
31373             }
31374         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31375             return this.select(s.parentNode);
31376         }
31377         return null;
31378     },
31379
31380     /**
31381      * Selects the node above the selected node in the tree, intelligently walking the nodes
31382      * @return TreeNode The new selection
31383      */
31384     selectNext : function(){
31385         var s = this.selNode || this.lastSelNode;
31386         if(!s){
31387             return null;
31388         }
31389         if(s.firstChild && s.isExpanded()){
31390              return this.select(s.firstChild);
31391          }else if(s.nextSibling){
31392              return this.select(s.nextSibling);
31393          }else if(s.parentNode){
31394             var newS = null;
31395             s.parentNode.bubble(function(){
31396                 if(this.nextSibling){
31397                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31398                     return false;
31399                 }
31400             });
31401             return newS;
31402          }
31403         return null;
31404     },
31405
31406     onKeyDown : function(e){
31407         var s = this.selNode || this.lastSelNode;
31408         // undesirable, but required
31409         var sm = this;
31410         if(!s){
31411             return;
31412         }
31413         var k = e.getKey();
31414         switch(k){
31415              case e.DOWN:
31416                  e.stopEvent();
31417                  this.selectNext();
31418              break;
31419              case e.UP:
31420                  e.stopEvent();
31421                  this.selectPrevious();
31422              break;
31423              case e.RIGHT:
31424                  e.preventDefault();
31425                  if(s.hasChildNodes()){
31426                      if(!s.isExpanded()){
31427                          s.expand();
31428                      }else if(s.firstChild){
31429                          this.select(s.firstChild, e);
31430                      }
31431                  }
31432              break;
31433              case e.LEFT:
31434                  e.preventDefault();
31435                  if(s.hasChildNodes() && s.isExpanded()){
31436                      s.collapse();
31437                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31438                      this.select(s.parentNode, e);
31439                  }
31440              break;
31441         };
31442     }
31443 });
31444
31445 /**
31446  * @class Roo.tree.MultiSelectionModel
31447  * @extends Roo.util.Observable
31448  * Multi selection for a TreePanel.
31449  */
31450 Roo.tree.MultiSelectionModel = function(){
31451    this.selNodes = [];
31452    this.selMap = {};
31453    this.addEvents({
31454        /**
31455         * @event selectionchange
31456         * Fires when the selected nodes change
31457         * @param {MultiSelectionModel} this
31458         * @param {Array} nodes Array of the selected nodes
31459         */
31460        "selectionchange" : true
31461    });
31462 };
31463
31464 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31465     init : function(tree){
31466         this.tree = tree;
31467         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31468         tree.on("click", this.onNodeClick, this);
31469     },
31470     
31471     onNodeClick : function(node, e){
31472         this.select(node, e, e.ctrlKey);
31473     },
31474     
31475     /**
31476      * Select a node.
31477      * @param {TreeNode} node The node to select
31478      * @param {EventObject} e (optional) An event associated with the selection
31479      * @param {Boolean} keepExisting True to retain existing selections
31480      * @return {TreeNode} The selected node
31481      */
31482     select : function(node, e, keepExisting){
31483         if(keepExisting !== true){
31484             this.clearSelections(true);
31485         }
31486         if(this.isSelected(node)){
31487             this.lastSelNode = node;
31488             return node;
31489         }
31490         this.selNodes.push(node);
31491         this.selMap[node.id] = node;
31492         this.lastSelNode = node;
31493         node.ui.onSelectedChange(true);
31494         this.fireEvent("selectionchange", this, this.selNodes);
31495         return node;
31496     },
31497     
31498     /**
31499      * Deselect a node.
31500      * @param {TreeNode} node The node to unselect
31501      */
31502     unselect : function(node){
31503         if(this.selMap[node.id]){
31504             node.ui.onSelectedChange(false);
31505             var sn = this.selNodes;
31506             var index = -1;
31507             if(sn.indexOf){
31508                 index = sn.indexOf(node);
31509             }else{
31510                 for(var i = 0, len = sn.length; i < len; i++){
31511                     if(sn[i] == node){
31512                         index = i;
31513                         break;
31514                     }
31515                 }
31516             }
31517             if(index != -1){
31518                 this.selNodes.splice(index, 1);
31519             }
31520             delete this.selMap[node.id];
31521             this.fireEvent("selectionchange", this, this.selNodes);
31522         }
31523     },
31524     
31525     /**
31526      * Clear all selections
31527      */
31528     clearSelections : function(suppressEvent){
31529         var sn = this.selNodes;
31530         if(sn.length > 0){
31531             for(var i = 0, len = sn.length; i < len; i++){
31532                 sn[i].ui.onSelectedChange(false);
31533             }
31534             this.selNodes = [];
31535             this.selMap = {};
31536             if(suppressEvent !== true){
31537                 this.fireEvent("selectionchange", this, this.selNodes);
31538             }
31539         }
31540     },
31541     
31542     /**
31543      * Returns true if the node is selected
31544      * @param {TreeNode} node The node to check
31545      * @return {Boolean}
31546      */
31547     isSelected : function(node){
31548         return this.selMap[node.id] ? true : false;  
31549     },
31550     
31551     /**
31552      * Returns an array of the selected nodes
31553      * @return {Array}
31554      */
31555     getSelectedNodes : function(){
31556         return this.selNodes;    
31557     },
31558
31559     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31560
31561     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31562
31563     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31564 });/*
31565  * Based on:
31566  * Ext JS Library 1.1.1
31567  * Copyright(c) 2006-2007, Ext JS, LLC.
31568  *
31569  * Originally Released Under LGPL - original licence link has changed is not relivant.
31570  *
31571  * Fork - LGPL
31572  * <script type="text/javascript">
31573  */
31574  
31575 /**
31576  * @class Roo.tree.TreeNode
31577  * @extends Roo.data.Node
31578  * @cfg {String} text The text for this node
31579  * @cfg {Boolean} expanded true to start the node expanded
31580  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31581  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31582  * @cfg {Boolean} disabled true to start the node disabled
31583  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31584  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31585  * @cfg {String} cls A css class to be added to the node
31586  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31587  * @cfg {String} href URL of the link used for the node (defaults to #)
31588  * @cfg {String} hrefTarget target frame for the link
31589  * @cfg {String} qtip An Ext QuickTip for the node
31590  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31591  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31592  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31593  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31594  * (defaults to undefined with no checkbox rendered)
31595  * @constructor
31596  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31597  */
31598 Roo.tree.TreeNode = function(attributes){
31599     attributes = attributes || {};
31600     if(typeof attributes == "string"){
31601         attributes = {text: attributes};
31602     }
31603     this.childrenRendered = false;
31604     this.rendered = false;
31605     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31606     this.expanded = attributes.expanded === true;
31607     this.isTarget = attributes.isTarget !== false;
31608     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31609     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31610
31611     /**
31612      * Read-only. The text for this node. To change it use setText().
31613      * @type String
31614      */
31615     this.text = attributes.text;
31616     /**
31617      * True if this node is disabled.
31618      * @type Boolean
31619      */
31620     this.disabled = attributes.disabled === true;
31621
31622     this.addEvents({
31623         /**
31624         * @event textchange
31625         * Fires when the text for this node is changed
31626         * @param {Node} this This node
31627         * @param {String} text The new text
31628         * @param {String} oldText The old text
31629         */
31630         "textchange" : true,
31631         /**
31632         * @event beforeexpand
31633         * Fires before this node is expanded, return false to cancel.
31634         * @param {Node} this This node
31635         * @param {Boolean} deep
31636         * @param {Boolean} anim
31637         */
31638         "beforeexpand" : true,
31639         /**
31640         * @event beforecollapse
31641         * Fires before this node is collapsed, return false to cancel.
31642         * @param {Node} this This node
31643         * @param {Boolean} deep
31644         * @param {Boolean} anim
31645         */
31646         "beforecollapse" : true,
31647         /**
31648         * @event expand
31649         * Fires when this node is expanded
31650         * @param {Node} this This node
31651         */
31652         "expand" : true,
31653         /**
31654         * @event disabledchange
31655         * Fires when the disabled status of this node changes
31656         * @param {Node} this This node
31657         * @param {Boolean} disabled
31658         */
31659         "disabledchange" : true,
31660         /**
31661         * @event collapse
31662         * Fires when this node is collapsed
31663         * @param {Node} this This node
31664         */
31665         "collapse" : true,
31666         /**
31667         * @event beforeclick
31668         * Fires before click processing. Return false to cancel the default action.
31669         * @param {Node} this This node
31670         * @param {Roo.EventObject} e The event object
31671         */
31672         "beforeclick":true,
31673         /**
31674         * @event checkchange
31675         * Fires when a node with a checkbox's checked property changes
31676         * @param {Node} this This node
31677         * @param {Boolean} checked
31678         */
31679         "checkchange":true,
31680         /**
31681         * @event click
31682         * Fires when this node is clicked
31683         * @param {Node} this This node
31684         * @param {Roo.EventObject} e The event object
31685         */
31686         "click":true,
31687         /**
31688         * @event dblclick
31689         * Fires when this node is double clicked
31690         * @param {Node} this This node
31691         * @param {Roo.EventObject} e The event object
31692         */
31693         "dblclick":true,
31694         /**
31695         * @event contextmenu
31696         * Fires when this node is right clicked
31697         * @param {Node} this This node
31698         * @param {Roo.EventObject} e The event object
31699         */
31700         "contextmenu":true,
31701         /**
31702         * @event beforechildrenrendered
31703         * Fires right before the child nodes for this node are rendered
31704         * @param {Node} this This node
31705         */
31706         "beforechildrenrendered":true
31707     });
31708
31709     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31710
31711     /**
31712      * Read-only. The UI for this node
31713      * @type TreeNodeUI
31714      */
31715     this.ui = new uiClass(this);
31716 };
31717 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31718     preventHScroll: true,
31719     /**
31720      * Returns true if this node is expanded
31721      * @return {Boolean}
31722      */
31723     isExpanded : function(){
31724         return this.expanded;
31725     },
31726
31727     /**
31728      * Returns the UI object for this node
31729      * @return {TreeNodeUI}
31730      */
31731     getUI : function(){
31732         return this.ui;
31733     },
31734
31735     // private override
31736     setFirstChild : function(node){
31737         var of = this.firstChild;
31738         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31739         if(this.childrenRendered && of && node != of){
31740             of.renderIndent(true, true);
31741         }
31742         if(this.rendered){
31743             this.renderIndent(true, true);
31744         }
31745     },
31746
31747     // private override
31748     setLastChild : function(node){
31749         var ol = this.lastChild;
31750         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31751         if(this.childrenRendered && ol && node != ol){
31752             ol.renderIndent(true, true);
31753         }
31754         if(this.rendered){
31755             this.renderIndent(true, true);
31756         }
31757     },
31758
31759     // these methods are overridden to provide lazy rendering support
31760     // private override
31761     appendChild : function(){
31762         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31763         if(node && this.childrenRendered){
31764             node.render();
31765         }
31766         this.ui.updateExpandIcon();
31767         return node;
31768     },
31769
31770     // private override
31771     removeChild : function(node){
31772         this.ownerTree.getSelectionModel().unselect(node);
31773         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31774         // if it's been rendered remove dom node
31775         if(this.childrenRendered){
31776             node.ui.remove();
31777         }
31778         if(this.childNodes.length < 1){
31779             this.collapse(false, false);
31780         }else{
31781             this.ui.updateExpandIcon();
31782         }
31783         if(!this.firstChild) {
31784             this.childrenRendered = false;
31785         }
31786         return node;
31787     },
31788
31789     // private override
31790     insertBefore : function(node, refNode){
31791         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31792         if(newNode && refNode && this.childrenRendered){
31793             node.render();
31794         }
31795         this.ui.updateExpandIcon();
31796         return newNode;
31797     },
31798
31799     /**
31800      * Sets the text for this node
31801      * @param {String} text
31802      */
31803     setText : function(text){
31804         var oldText = this.text;
31805         this.text = text;
31806         this.attributes.text = text;
31807         if(this.rendered){ // event without subscribing
31808             this.ui.onTextChange(this, text, oldText);
31809         }
31810         this.fireEvent("textchange", this, text, oldText);
31811     },
31812
31813     /**
31814      * Triggers selection of this node
31815      */
31816     select : function(){
31817         this.getOwnerTree().getSelectionModel().select(this);
31818     },
31819
31820     /**
31821      * Triggers deselection of this node
31822      */
31823     unselect : function(){
31824         this.getOwnerTree().getSelectionModel().unselect(this);
31825     },
31826
31827     /**
31828      * Returns true if this node is selected
31829      * @return {Boolean}
31830      */
31831     isSelected : function(){
31832         return this.getOwnerTree().getSelectionModel().isSelected(this);
31833     },
31834
31835     /**
31836      * Expand this node.
31837      * @param {Boolean} deep (optional) True to expand all children as well
31838      * @param {Boolean} anim (optional) false to cancel the default animation
31839      * @param {Function} callback (optional) A callback to be called when
31840      * expanding this node completes (does not wait for deep expand to complete).
31841      * Called with 1 parameter, this node.
31842      */
31843     expand : function(deep, anim, callback){
31844         if(!this.expanded){
31845             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31846                 return;
31847             }
31848             if(!this.childrenRendered){
31849                 this.renderChildren();
31850             }
31851             this.expanded = true;
31852             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31853                 this.ui.animExpand(function(){
31854                     this.fireEvent("expand", this);
31855                     if(typeof callback == "function"){
31856                         callback(this);
31857                     }
31858                     if(deep === true){
31859                         this.expandChildNodes(true);
31860                     }
31861                 }.createDelegate(this));
31862                 return;
31863             }else{
31864                 this.ui.expand();
31865                 this.fireEvent("expand", this);
31866                 if(typeof callback == "function"){
31867                     callback(this);
31868                 }
31869             }
31870         }else{
31871            if(typeof callback == "function"){
31872                callback(this);
31873            }
31874         }
31875         if(deep === true){
31876             this.expandChildNodes(true);
31877         }
31878     },
31879
31880     isHiddenRoot : function(){
31881         return this.isRoot && !this.getOwnerTree().rootVisible;
31882     },
31883
31884     /**
31885      * Collapse this node.
31886      * @param {Boolean} deep (optional) True to collapse all children as well
31887      * @param {Boolean} anim (optional) false to cancel the default animation
31888      */
31889     collapse : function(deep, anim){
31890         if(this.expanded && !this.isHiddenRoot()){
31891             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31892                 return;
31893             }
31894             this.expanded = false;
31895             if((this.getOwnerTree().animate && anim !== false) || anim){
31896                 this.ui.animCollapse(function(){
31897                     this.fireEvent("collapse", this);
31898                     if(deep === true){
31899                         this.collapseChildNodes(true);
31900                     }
31901                 }.createDelegate(this));
31902                 return;
31903             }else{
31904                 this.ui.collapse();
31905                 this.fireEvent("collapse", this);
31906             }
31907         }
31908         if(deep === true){
31909             var cs = this.childNodes;
31910             for(var i = 0, len = cs.length; i < len; i++) {
31911                 cs[i].collapse(true, false);
31912             }
31913         }
31914     },
31915
31916     // private
31917     delayedExpand : function(delay){
31918         if(!this.expandProcId){
31919             this.expandProcId = this.expand.defer(delay, this);
31920         }
31921     },
31922
31923     // private
31924     cancelExpand : function(){
31925         if(this.expandProcId){
31926             clearTimeout(this.expandProcId);
31927         }
31928         this.expandProcId = false;
31929     },
31930
31931     /**
31932      * Toggles expanded/collapsed state of the node
31933      */
31934     toggle : function(){
31935         if(this.expanded){
31936             this.collapse();
31937         }else{
31938             this.expand();
31939         }
31940     },
31941
31942     /**
31943      * Ensures all parent nodes are expanded
31944      */
31945     ensureVisible : function(callback){
31946         var tree = this.getOwnerTree();
31947         tree.expandPath(this.parentNode.getPath(), false, function(){
31948             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31949             Roo.callback(callback);
31950         }.createDelegate(this));
31951     },
31952
31953     /**
31954      * Expand all child nodes
31955      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31956      */
31957     expandChildNodes : function(deep){
31958         var cs = this.childNodes;
31959         for(var i = 0, len = cs.length; i < len; i++) {
31960                 cs[i].expand(deep);
31961         }
31962     },
31963
31964     /**
31965      * Collapse all child nodes
31966      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31967      */
31968     collapseChildNodes : function(deep){
31969         var cs = this.childNodes;
31970         for(var i = 0, len = cs.length; i < len; i++) {
31971                 cs[i].collapse(deep);
31972         }
31973     },
31974
31975     /**
31976      * Disables this node
31977      */
31978     disable : function(){
31979         this.disabled = true;
31980         this.unselect();
31981         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31982             this.ui.onDisableChange(this, true);
31983         }
31984         this.fireEvent("disabledchange", this, true);
31985     },
31986
31987     /**
31988      * Enables this node
31989      */
31990     enable : function(){
31991         this.disabled = false;
31992         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31993             this.ui.onDisableChange(this, false);
31994         }
31995         this.fireEvent("disabledchange", this, false);
31996     },
31997
31998     // private
31999     renderChildren : function(suppressEvent){
32000         if(suppressEvent !== false){
32001             this.fireEvent("beforechildrenrendered", this);
32002         }
32003         var cs = this.childNodes;
32004         for(var i = 0, len = cs.length; i < len; i++){
32005             cs[i].render(true);
32006         }
32007         this.childrenRendered = true;
32008     },
32009
32010     // private
32011     sort : function(fn, scope){
32012         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32013         if(this.childrenRendered){
32014             var cs = this.childNodes;
32015             for(var i = 0, len = cs.length; i < len; i++){
32016                 cs[i].render(true);
32017             }
32018         }
32019     },
32020
32021     // private
32022     render : function(bulkRender){
32023         this.ui.render(bulkRender);
32024         if(!this.rendered){
32025             this.rendered = true;
32026             if(this.expanded){
32027                 this.expanded = false;
32028                 this.expand(false, false);
32029             }
32030         }
32031     },
32032
32033     // private
32034     renderIndent : function(deep, refresh){
32035         if(refresh){
32036             this.ui.childIndent = null;
32037         }
32038         this.ui.renderIndent();
32039         if(deep === true && this.childrenRendered){
32040             var cs = this.childNodes;
32041             for(var i = 0, len = cs.length; i < len; i++){
32042                 cs[i].renderIndent(true, refresh);
32043             }
32044         }
32045     }
32046 });/*
32047  * Based on:
32048  * Ext JS Library 1.1.1
32049  * Copyright(c) 2006-2007, Ext JS, LLC.
32050  *
32051  * Originally Released Under LGPL - original licence link has changed is not relivant.
32052  *
32053  * Fork - LGPL
32054  * <script type="text/javascript">
32055  */
32056  
32057 /**
32058  * @class Roo.tree.AsyncTreeNode
32059  * @extends Roo.tree.TreeNode
32060  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32061  * @constructor
32062  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32063  */
32064  Roo.tree.AsyncTreeNode = function(config){
32065     this.loaded = false;
32066     this.loading = false;
32067     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32068     /**
32069     * @event beforeload
32070     * Fires before this node is loaded, return false to cancel
32071     * @param {Node} this This node
32072     */
32073     this.addEvents({'beforeload':true, 'load': true});
32074     /**
32075     * @event load
32076     * Fires when this node is loaded
32077     * @param {Node} this This node
32078     */
32079     /**
32080      * The loader used by this node (defaults to using the tree's defined loader)
32081      * @type TreeLoader
32082      * @property loader
32083      */
32084 };
32085 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32086     expand : function(deep, anim, callback){
32087         if(this.loading){ // if an async load is already running, waiting til it's done
32088             var timer;
32089             var f = function(){
32090                 if(!this.loading){ // done loading
32091                     clearInterval(timer);
32092                     this.expand(deep, anim, callback);
32093                 }
32094             }.createDelegate(this);
32095             timer = setInterval(f, 200);
32096             return;
32097         }
32098         if(!this.loaded){
32099             if(this.fireEvent("beforeload", this) === false){
32100                 return;
32101             }
32102             this.loading = true;
32103             this.ui.beforeLoad(this);
32104             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32105             if(loader){
32106                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32107                 return;
32108             }
32109         }
32110         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32111     },
32112     
32113     /**
32114      * Returns true if this node is currently loading
32115      * @return {Boolean}
32116      */
32117     isLoading : function(){
32118         return this.loading;  
32119     },
32120     
32121     loadComplete : function(deep, anim, callback){
32122         this.loading = false;
32123         this.loaded = true;
32124         this.ui.afterLoad(this);
32125         this.fireEvent("load", this);
32126         this.expand(deep, anim, callback);
32127     },
32128     
32129     /**
32130      * Returns true if this node has been loaded
32131      * @return {Boolean}
32132      */
32133     isLoaded : function(){
32134         return this.loaded;
32135     },
32136     
32137     hasChildNodes : function(){
32138         if(!this.isLeaf() && !this.loaded){
32139             return true;
32140         }else{
32141             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32142         }
32143     },
32144
32145     /**
32146      * Trigger a reload for this node
32147      * @param {Function} callback
32148      */
32149     reload : function(callback){
32150         this.collapse(false, false);
32151         while(this.firstChild){
32152             this.removeChild(this.firstChild);
32153         }
32154         this.childrenRendered = false;
32155         this.loaded = false;
32156         if(this.isHiddenRoot()){
32157             this.expanded = false;
32158         }
32159         this.expand(false, false, callback);
32160     }
32161 });/*
32162  * Based on:
32163  * Ext JS Library 1.1.1
32164  * Copyright(c) 2006-2007, Ext JS, LLC.
32165  *
32166  * Originally Released Under LGPL - original licence link has changed is not relivant.
32167  *
32168  * Fork - LGPL
32169  * <script type="text/javascript">
32170  */
32171  
32172 /**
32173  * @class Roo.tree.TreeNodeUI
32174  * @constructor
32175  * @param {Object} node The node to render
32176  * The TreeNode UI implementation is separate from the
32177  * tree implementation. Unless you are customizing the tree UI,
32178  * you should never have to use this directly.
32179  */
32180 Roo.tree.TreeNodeUI = function(node){
32181     this.node = node;
32182     this.rendered = false;
32183     this.animating = false;
32184     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32185 };
32186
32187 Roo.tree.TreeNodeUI.prototype = {
32188     removeChild : function(node){
32189         if(this.rendered){
32190             this.ctNode.removeChild(node.ui.getEl());
32191         }
32192     },
32193
32194     beforeLoad : function(){
32195          this.addClass("x-tree-node-loading");
32196     },
32197
32198     afterLoad : function(){
32199          this.removeClass("x-tree-node-loading");
32200     },
32201
32202     onTextChange : function(node, text, oldText){
32203         if(this.rendered){
32204             this.textNode.innerHTML = text;
32205         }
32206     },
32207
32208     onDisableChange : function(node, state){
32209         this.disabled = state;
32210         if(state){
32211             this.addClass("x-tree-node-disabled");
32212         }else{
32213             this.removeClass("x-tree-node-disabled");
32214         }
32215     },
32216
32217     onSelectedChange : function(state){
32218         if(state){
32219             this.focus();
32220             this.addClass("x-tree-selected");
32221         }else{
32222             //this.blur();
32223             this.removeClass("x-tree-selected");
32224         }
32225     },
32226
32227     onMove : function(tree, node, oldParent, newParent, index, refNode){
32228         this.childIndent = null;
32229         if(this.rendered){
32230             var targetNode = newParent.ui.getContainer();
32231             if(!targetNode){//target not rendered
32232                 this.holder = document.createElement("div");
32233                 this.holder.appendChild(this.wrap);
32234                 return;
32235             }
32236             var insertBefore = refNode ? refNode.ui.getEl() : null;
32237             if(insertBefore){
32238                 targetNode.insertBefore(this.wrap, insertBefore);
32239             }else{
32240                 targetNode.appendChild(this.wrap);
32241             }
32242             this.node.renderIndent(true);
32243         }
32244     },
32245
32246     addClass : function(cls){
32247         if(this.elNode){
32248             Roo.fly(this.elNode).addClass(cls);
32249         }
32250     },
32251
32252     removeClass : function(cls){
32253         if(this.elNode){
32254             Roo.fly(this.elNode).removeClass(cls);
32255         }
32256     },
32257
32258     remove : function(){
32259         if(this.rendered){
32260             this.holder = document.createElement("div");
32261             this.holder.appendChild(this.wrap);
32262         }
32263     },
32264
32265     fireEvent : function(){
32266         return this.node.fireEvent.apply(this.node, arguments);
32267     },
32268
32269     initEvents : function(){
32270         this.node.on("move", this.onMove, this);
32271         var E = Roo.EventManager;
32272         var a = this.anchor;
32273
32274         var el = Roo.fly(a, '_treeui');
32275
32276         if(Roo.isOpera){ // opera render bug ignores the CSS
32277             el.setStyle("text-decoration", "none");
32278         }
32279
32280         el.on("click", this.onClick, this);
32281         el.on("dblclick", this.onDblClick, this);
32282
32283         if(this.checkbox){
32284             Roo.EventManager.on(this.checkbox,
32285                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32286         }
32287
32288         el.on("contextmenu", this.onContextMenu, this);
32289
32290         var icon = Roo.fly(this.iconNode);
32291         icon.on("click", this.onClick, this);
32292         icon.on("dblclick", this.onDblClick, this);
32293         icon.on("contextmenu", this.onContextMenu, this);
32294         E.on(this.ecNode, "click", this.ecClick, this, true);
32295
32296         if(this.node.disabled){
32297             this.addClass("x-tree-node-disabled");
32298         }
32299         if(this.node.hidden){
32300             this.addClass("x-tree-node-disabled");
32301         }
32302         var ot = this.node.getOwnerTree();
32303         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32304         if(dd && (!this.node.isRoot || ot.rootVisible)){
32305             Roo.dd.Registry.register(this.elNode, {
32306                 node: this.node,
32307                 handles: this.getDDHandles(),
32308                 isHandle: false
32309             });
32310         }
32311     },
32312
32313     getDDHandles : function(){
32314         return [this.iconNode, this.textNode];
32315     },
32316
32317     hide : function(){
32318         if(this.rendered){
32319             this.wrap.style.display = "none";
32320         }
32321     },
32322
32323     show : function(){
32324         if(this.rendered){
32325             this.wrap.style.display = "";
32326         }
32327     },
32328
32329     onContextMenu : function(e){
32330         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32331             e.preventDefault();
32332             this.focus();
32333             this.fireEvent("contextmenu", this.node, e);
32334         }
32335     },
32336
32337     onClick : function(e){
32338         if(this.dropping){
32339             e.stopEvent();
32340             return;
32341         }
32342         if(this.fireEvent("beforeclick", this.node, e) !== false){
32343             if(!this.disabled && this.node.attributes.href){
32344                 this.fireEvent("click", this.node, e);
32345                 return;
32346             }
32347             e.preventDefault();
32348             if(this.disabled){
32349                 return;
32350             }
32351
32352             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32353                 this.node.toggle();
32354             }
32355
32356             this.fireEvent("click", this.node, e);
32357         }else{
32358             e.stopEvent();
32359         }
32360     },
32361
32362     onDblClick : function(e){
32363         e.preventDefault();
32364         if(this.disabled){
32365             return;
32366         }
32367         if(this.checkbox){
32368             this.toggleCheck();
32369         }
32370         if(!this.animating && this.node.hasChildNodes()){
32371             this.node.toggle();
32372         }
32373         this.fireEvent("dblclick", this.node, e);
32374     },
32375
32376     onCheckChange : function(){
32377         var checked = this.checkbox.checked;
32378         this.node.attributes.checked = checked;
32379         this.fireEvent('checkchange', this.node, checked);
32380     },
32381
32382     ecClick : function(e){
32383         if(!this.animating && this.node.hasChildNodes()){
32384             this.node.toggle();
32385         }
32386     },
32387
32388     startDrop : function(){
32389         this.dropping = true;
32390     },
32391
32392     // delayed drop so the click event doesn't get fired on a drop
32393     endDrop : function(){
32394        setTimeout(function(){
32395            this.dropping = false;
32396        }.createDelegate(this), 50);
32397     },
32398
32399     expand : function(){
32400         this.updateExpandIcon();
32401         this.ctNode.style.display = "";
32402     },
32403
32404     focus : function(){
32405         if(!this.node.preventHScroll){
32406             try{this.anchor.focus();
32407             }catch(e){}
32408         }else if(!Roo.isIE){
32409             try{
32410                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32411                 var l = noscroll.scrollLeft;
32412                 this.anchor.focus();
32413                 noscroll.scrollLeft = l;
32414             }catch(e){}
32415         }
32416     },
32417
32418     toggleCheck : function(value){
32419         var cb = this.checkbox;
32420         if(cb){
32421             cb.checked = (value === undefined ? !cb.checked : value);
32422         }
32423     },
32424
32425     blur : function(){
32426         try{
32427             this.anchor.blur();
32428         }catch(e){}
32429     },
32430
32431     animExpand : function(callback){
32432         var ct = Roo.get(this.ctNode);
32433         ct.stopFx();
32434         if(!this.node.hasChildNodes()){
32435             this.updateExpandIcon();
32436             this.ctNode.style.display = "";
32437             Roo.callback(callback);
32438             return;
32439         }
32440         this.animating = true;
32441         this.updateExpandIcon();
32442
32443         ct.slideIn('t', {
32444            callback : function(){
32445                this.animating = false;
32446                Roo.callback(callback);
32447             },
32448             scope: this,
32449             duration: this.node.ownerTree.duration || .25
32450         });
32451     },
32452
32453     highlight : function(){
32454         var tree = this.node.getOwnerTree();
32455         Roo.fly(this.wrap).highlight(
32456             tree.hlColor || "C3DAF9",
32457             {endColor: tree.hlBaseColor}
32458         );
32459     },
32460
32461     collapse : function(){
32462         this.updateExpandIcon();
32463         this.ctNode.style.display = "none";
32464     },
32465
32466     animCollapse : function(callback){
32467         var ct = Roo.get(this.ctNode);
32468         ct.enableDisplayMode('block');
32469         ct.stopFx();
32470
32471         this.animating = true;
32472         this.updateExpandIcon();
32473
32474         ct.slideOut('t', {
32475             callback : function(){
32476                this.animating = false;
32477                Roo.callback(callback);
32478             },
32479             scope: this,
32480             duration: this.node.ownerTree.duration || .25
32481         });
32482     },
32483
32484     getContainer : function(){
32485         return this.ctNode;
32486     },
32487
32488     getEl : function(){
32489         return this.wrap;
32490     },
32491
32492     appendDDGhost : function(ghostNode){
32493         ghostNode.appendChild(this.elNode.cloneNode(true));
32494     },
32495
32496     getDDRepairXY : function(){
32497         return Roo.lib.Dom.getXY(this.iconNode);
32498     },
32499
32500     onRender : function(){
32501         this.render();
32502     },
32503
32504     render : function(bulkRender){
32505         var n = this.node, a = n.attributes;
32506         var targetNode = n.parentNode ?
32507               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32508
32509         if(!this.rendered){
32510             this.rendered = true;
32511
32512             this.renderElements(n, a, targetNode, bulkRender);
32513
32514             if(a.qtip){
32515                if(this.textNode.setAttributeNS){
32516                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32517                    if(a.qtipTitle){
32518                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32519                    }
32520                }else{
32521                    this.textNode.setAttribute("ext:qtip", a.qtip);
32522                    if(a.qtipTitle){
32523                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32524                    }
32525                }
32526             }else if(a.qtipCfg){
32527                 a.qtipCfg.target = Roo.id(this.textNode);
32528                 Roo.QuickTips.register(a.qtipCfg);
32529             }
32530             this.initEvents();
32531             if(!this.node.expanded){
32532                 this.updateExpandIcon();
32533             }
32534         }else{
32535             if(bulkRender === true) {
32536                 targetNode.appendChild(this.wrap);
32537             }
32538         }
32539     },
32540
32541     renderElements : function(n, a, targetNode, bulkRender){
32542         // add some indent caching, this helps performance when rendering a large tree
32543         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32544         var t = n.getOwnerTree();
32545         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32546         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32547         var cb = typeof a.checked == 'boolean';
32548         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32549         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32550             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32551             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32552             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32553             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32554             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32555              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32556                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32557             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32558             "</li>"];
32559
32560         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32561             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32562                                 n.nextSibling.ui.getEl(), buf.join(""));
32563         }else{
32564             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32565         }
32566
32567         this.elNode = this.wrap.childNodes[0];
32568         this.ctNode = this.wrap.childNodes[1];
32569         var cs = this.elNode.childNodes;
32570         this.indentNode = cs[0];
32571         this.ecNode = cs[1];
32572         this.iconNode = cs[2];
32573         var index = 3;
32574         if(cb){
32575             this.checkbox = cs[3];
32576             index++;
32577         }
32578         this.anchor = cs[index];
32579         this.textNode = cs[index].firstChild;
32580     },
32581
32582     getAnchor : function(){
32583         return this.anchor;
32584     },
32585
32586     getTextEl : function(){
32587         return this.textNode;
32588     },
32589
32590     getIconEl : function(){
32591         return this.iconNode;
32592     },
32593
32594     isChecked : function(){
32595         return this.checkbox ? this.checkbox.checked : false;
32596     },
32597
32598     updateExpandIcon : function(){
32599         if(this.rendered){
32600             var n = this.node, c1, c2;
32601             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32602             var hasChild = n.hasChildNodes();
32603             if(hasChild){
32604                 if(n.expanded){
32605                     cls += "-minus";
32606                     c1 = "x-tree-node-collapsed";
32607                     c2 = "x-tree-node-expanded";
32608                 }else{
32609                     cls += "-plus";
32610                     c1 = "x-tree-node-expanded";
32611                     c2 = "x-tree-node-collapsed";
32612                 }
32613                 if(this.wasLeaf){
32614                     this.removeClass("x-tree-node-leaf");
32615                     this.wasLeaf = false;
32616                 }
32617                 if(this.c1 != c1 || this.c2 != c2){
32618                     Roo.fly(this.elNode).replaceClass(c1, c2);
32619                     this.c1 = c1; this.c2 = c2;
32620                 }
32621             }else{
32622                 if(!this.wasLeaf){
32623                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32624                     delete this.c1;
32625                     delete this.c2;
32626                     this.wasLeaf = true;
32627                 }
32628             }
32629             var ecc = "x-tree-ec-icon "+cls;
32630             if(this.ecc != ecc){
32631                 this.ecNode.className = ecc;
32632                 this.ecc = ecc;
32633             }
32634         }
32635     },
32636
32637     getChildIndent : function(){
32638         if(!this.childIndent){
32639             var buf = [];
32640             var p = this.node;
32641             while(p){
32642                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32643                     if(!p.isLast()) {
32644                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32645                     } else {
32646                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32647                     }
32648                 }
32649                 p = p.parentNode;
32650             }
32651             this.childIndent = buf.join("");
32652         }
32653         return this.childIndent;
32654     },
32655
32656     renderIndent : function(){
32657         if(this.rendered){
32658             var indent = "";
32659             var p = this.node.parentNode;
32660             if(p){
32661                 indent = p.ui.getChildIndent();
32662             }
32663             if(this.indentMarkup != indent){ // don't rerender if not required
32664                 this.indentNode.innerHTML = indent;
32665                 this.indentMarkup = indent;
32666             }
32667             this.updateExpandIcon();
32668         }
32669     }
32670 };
32671
32672 Roo.tree.RootTreeNodeUI = function(){
32673     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32674 };
32675 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32676     render : function(){
32677         if(!this.rendered){
32678             var targetNode = this.node.ownerTree.innerCt.dom;
32679             this.node.expanded = true;
32680             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32681             this.wrap = this.ctNode = targetNode.firstChild;
32682         }
32683     },
32684     collapse : function(){
32685     },
32686     expand : function(){
32687     }
32688 });/*
32689  * Based on:
32690  * Ext JS Library 1.1.1
32691  * Copyright(c) 2006-2007, Ext JS, LLC.
32692  *
32693  * Originally Released Under LGPL - original licence link has changed is not relivant.
32694  *
32695  * Fork - LGPL
32696  * <script type="text/javascript">
32697  */
32698 /**
32699  * @class Roo.tree.TreeLoader
32700  * @extends Roo.util.Observable
32701  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32702  * nodes from a specified URL. The response must be a javascript Array definition
32703  * who's elements are node definition objects. eg:
32704  * <pre><code>
32705    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32706     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32707 </code></pre>
32708  * <br><br>
32709  * A server request is sent, and child nodes are loaded only when a node is expanded.
32710  * The loading node's id is passed to the server under the parameter name "node" to
32711  * enable the server to produce the correct child nodes.
32712  * <br><br>
32713  * To pass extra parameters, an event handler may be attached to the "beforeload"
32714  * event, and the parameters specified in the TreeLoader's baseParams property:
32715  * <pre><code>
32716     myTreeLoader.on("beforeload", function(treeLoader, node) {
32717         this.baseParams.category = node.attributes.category;
32718     }, this);
32719 </code></pre><
32720  * This would pass an HTTP parameter called "category" to the server containing
32721  * the value of the Node's "category" attribute.
32722  * @constructor
32723  * Creates a new Treeloader.
32724  * @param {Object} config A config object containing config properties.
32725  */
32726 Roo.tree.TreeLoader = function(config){
32727     this.baseParams = {};
32728     this.requestMethod = "POST";
32729     Roo.apply(this, config);
32730
32731     this.addEvents({
32732     
32733         /**
32734          * @event beforeload
32735          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32736          * @param {Object} This TreeLoader object.
32737          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32738          * @param {Object} callback The callback function specified in the {@link #load} call.
32739          */
32740         beforeload : true,
32741         /**
32742          * @event load
32743          * Fires when the node has been successfuly loaded.
32744          * @param {Object} This TreeLoader object.
32745          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32746          * @param {Object} response The response object containing the data from the server.
32747          */
32748         load : true,
32749         /**
32750          * @event loadexception
32751          * Fires if the network request failed.
32752          * @param {Object} This TreeLoader object.
32753          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32754          * @param {Object} response The response object containing the data from the server.
32755          */
32756         loadexception : true,
32757         /**
32758          * @event create
32759          * Fires before a node is created, enabling you to return custom Node types 
32760          * @param {Object} This TreeLoader object.
32761          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32762          */
32763         create : true
32764     });
32765
32766     Roo.tree.TreeLoader.superclass.constructor.call(this);
32767 };
32768
32769 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32770     /**
32771     * @cfg {String} dataUrl The URL from which to request a Json string which
32772     * specifies an array of node definition object representing the child nodes
32773     * to be loaded.
32774     */
32775     /**
32776     * @cfg {Object} baseParams (optional) An object containing properties which
32777     * specify HTTP parameters to be passed to each request for child nodes.
32778     */
32779     /**
32780     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32781     * created by this loader. If the attributes sent by the server have an attribute in this object,
32782     * they take priority.
32783     */
32784     /**
32785     * @cfg {Object} uiProviders (optional) An object containing properties which
32786     * 
32787     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32788     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32789     * <i>uiProvider</i> attribute of a returned child node is a string rather
32790     * than a reference to a TreeNodeUI implementation, this that string value
32791     * is used as a property name in the uiProviders object. You can define the provider named
32792     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32793     */
32794     uiProviders : {},
32795
32796     /**
32797     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32798     * child nodes before loading.
32799     */
32800     clearOnLoad : true,
32801
32802     /**
32803     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32804     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32805     * Grid query { data : [ .....] }
32806     */
32807     
32808     root : false,
32809      /**
32810     * @cfg {String} queryParam (optional) 
32811     * Name of the query as it will be passed on the querystring (defaults to 'node')
32812     * eg. the request will be ?node=[id]
32813     */
32814     
32815     
32816     queryParam: false,
32817     
32818     /**
32819      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32820      * This is called automatically when a node is expanded, but may be used to reload
32821      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32822      * @param {Roo.tree.TreeNode} node
32823      * @param {Function} callback
32824      */
32825     load : function(node, callback){
32826         if(this.clearOnLoad){
32827             while(node.firstChild){
32828                 node.removeChild(node.firstChild);
32829             }
32830         }
32831         if(node.attributes.children){ // preloaded json children
32832             var cs = node.attributes.children;
32833             for(var i = 0, len = cs.length; i < len; i++){
32834                 node.appendChild(this.createNode(cs[i]));
32835             }
32836             if(typeof callback == "function"){
32837                 callback();
32838             }
32839         }else if(this.dataUrl){
32840             this.requestData(node, callback);
32841         }
32842     },
32843
32844     getParams: function(node){
32845         var buf = [], bp = this.baseParams;
32846         for(var key in bp){
32847             if(typeof bp[key] != "function"){
32848                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32849             }
32850         }
32851         var n = this.queryParam === false ? 'node' : this.queryParam;
32852         buf.push(n + "=", encodeURIComponent(node.id));
32853         return buf.join("");
32854     },
32855
32856     requestData : function(node, callback){
32857         if(this.fireEvent("beforeload", this, node, callback) !== false){
32858             this.transId = Roo.Ajax.request({
32859                 method:this.requestMethod,
32860                 url: this.dataUrl||this.url,
32861                 success: this.handleResponse,
32862                 failure: this.handleFailure,
32863                 scope: this,
32864                 argument: {callback: callback, node: node},
32865                 params: this.getParams(node)
32866             });
32867         }else{
32868             // if the load is cancelled, make sure we notify
32869             // the node that we are done
32870             if(typeof callback == "function"){
32871                 callback();
32872             }
32873         }
32874     },
32875
32876     isLoading : function(){
32877         return this.transId ? true : false;
32878     },
32879
32880     abort : function(){
32881         if(this.isLoading()){
32882             Roo.Ajax.abort(this.transId);
32883         }
32884     },
32885
32886     // private
32887     createNode : function(attr){
32888         // apply baseAttrs, nice idea Corey!
32889         if(this.baseAttrs){
32890             Roo.applyIf(attr, this.baseAttrs);
32891         }
32892         if(this.applyLoader !== false){
32893             attr.loader = this;
32894         }
32895         // uiProvider = depreciated..
32896         
32897         if(typeof(attr.uiProvider) == 'string'){
32898            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32899                 /**  eval:var:attr */ eval(attr.uiProvider);
32900         }
32901         if(typeof(this.uiProviders['default']) != 'undefined') {
32902             attr.uiProvider = this.uiProviders['default'];
32903         }
32904         
32905         this.fireEvent('create', this, attr);
32906         
32907         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32908         return(attr.leaf ?
32909                         new Roo.tree.TreeNode(attr) :
32910                         new Roo.tree.AsyncTreeNode(attr));
32911     },
32912
32913     processResponse : function(response, node, callback){
32914         var json = response.responseText;
32915         try {
32916             
32917             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32918             if (this.root !== false) {
32919                 o = o[this.root];
32920             }
32921             
32922             for(var i = 0, len = o.length; i < len; i++){
32923                 var n = this.createNode(o[i]);
32924                 if(n){
32925                     node.appendChild(n);
32926                 }
32927             }
32928             if(typeof callback == "function"){
32929                 callback(this, node);
32930             }
32931         }catch(e){
32932             this.handleFailure(response);
32933         }
32934     },
32935
32936     handleResponse : function(response){
32937         this.transId = false;
32938         var a = response.argument;
32939         this.processResponse(response, a.node, a.callback);
32940         this.fireEvent("load", this, a.node, response);
32941     },
32942
32943     handleFailure : function(response){
32944         this.transId = false;
32945         var a = response.argument;
32946         this.fireEvent("loadexception", this, a.node, response);
32947         if(typeof a.callback == "function"){
32948             a.callback(this, a.node);
32949         }
32950     }
32951 });/*
32952  * Based on:
32953  * Ext JS Library 1.1.1
32954  * Copyright(c) 2006-2007, Ext JS, LLC.
32955  *
32956  * Originally Released Under LGPL - original licence link has changed is not relivant.
32957  *
32958  * Fork - LGPL
32959  * <script type="text/javascript">
32960  */
32961
32962 /**
32963 * @class Roo.tree.TreeFilter
32964 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32965 * @param {TreePanel} tree
32966 * @param {Object} config (optional)
32967  */
32968 Roo.tree.TreeFilter = function(tree, config){
32969     this.tree = tree;
32970     this.filtered = {};
32971     Roo.apply(this, config);
32972 };
32973
32974 Roo.tree.TreeFilter.prototype = {
32975     clearBlank:false,
32976     reverse:false,
32977     autoClear:false,
32978     remove:false,
32979
32980      /**
32981      * Filter the data by a specific attribute.
32982      * @param {String/RegExp} value Either string that the attribute value
32983      * should start with or a RegExp to test against the attribute
32984      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
32985      * @param {TreeNode} startNode (optional) The node to start the filter at.
32986      */
32987     filter : function(value, attr, startNode){
32988         attr = attr || "text";
32989         var f;
32990         if(typeof value == "string"){
32991             var vlen = value.length;
32992             // auto clear empty filter
32993             if(vlen == 0 && this.clearBlank){
32994                 this.clear();
32995                 return;
32996             }
32997             value = value.toLowerCase();
32998             f = function(n){
32999                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33000             };
33001         }else if(value.exec){ // regex?
33002             f = function(n){
33003                 return value.test(n.attributes[attr]);
33004             };
33005         }else{
33006             throw 'Illegal filter type, must be string or regex';
33007         }
33008         this.filterBy(f, null, startNode);
33009         },
33010
33011     /**
33012      * Filter by a function. The passed function will be called with each
33013      * node in the tree (or from the startNode). If the function returns true, the node is kept
33014      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33015      * @param {Function} fn The filter function
33016      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33017      */
33018     filterBy : function(fn, scope, startNode){
33019         startNode = startNode || this.tree.root;
33020         if(this.autoClear){
33021             this.clear();
33022         }
33023         var af = this.filtered, rv = this.reverse;
33024         var f = function(n){
33025             if(n == startNode){
33026                 return true;
33027             }
33028             if(af[n.id]){
33029                 return false;
33030             }
33031             var m = fn.call(scope || n, n);
33032             if(!m || rv){
33033                 af[n.id] = n;
33034                 n.ui.hide();
33035                 return false;
33036             }
33037             return true;
33038         };
33039         startNode.cascade(f);
33040         if(this.remove){
33041            for(var id in af){
33042                if(typeof id != "function"){
33043                    var n = af[id];
33044                    if(n && n.parentNode){
33045                        n.parentNode.removeChild(n);
33046                    }
33047                }
33048            }
33049         }
33050     },
33051
33052     /**
33053      * Clears the current filter. Note: with the "remove" option
33054      * set a filter cannot be cleared.
33055      */
33056     clear : function(){
33057         var t = this.tree;
33058         var af = this.filtered;
33059         for(var id in af){
33060             if(typeof id != "function"){
33061                 var n = af[id];
33062                 if(n){
33063                     n.ui.show();
33064                 }
33065             }
33066         }
33067         this.filtered = {};
33068     }
33069 };
33070 /*
33071  * Based on:
33072  * Ext JS Library 1.1.1
33073  * Copyright(c) 2006-2007, Ext JS, LLC.
33074  *
33075  * Originally Released Under LGPL - original licence link has changed is not relivant.
33076  *
33077  * Fork - LGPL
33078  * <script type="text/javascript">
33079  */
33080  
33081
33082 /**
33083  * @class Roo.tree.TreeSorter
33084  * Provides sorting of nodes in a TreePanel
33085  * 
33086  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33087  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33088  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33089  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33090  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33091  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33092  * @constructor
33093  * @param {TreePanel} tree
33094  * @param {Object} config
33095  */
33096 Roo.tree.TreeSorter = function(tree, config){
33097     Roo.apply(this, config);
33098     tree.on("beforechildrenrendered", this.doSort, this);
33099     tree.on("append", this.updateSort, this);
33100     tree.on("insert", this.updateSort, this);
33101     
33102     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33103     var p = this.property || "text";
33104     var sortType = this.sortType;
33105     var fs = this.folderSort;
33106     var cs = this.caseSensitive === true;
33107     var leafAttr = this.leafAttr || 'leaf';
33108
33109     this.sortFn = function(n1, n2){
33110         if(fs){
33111             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33112                 return 1;
33113             }
33114             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33115                 return -1;
33116             }
33117         }
33118         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33119         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33120         if(v1 < v2){
33121                         return dsc ? +1 : -1;
33122                 }else if(v1 > v2){
33123                         return dsc ? -1 : +1;
33124         }else{
33125                 return 0;
33126         }
33127     };
33128 };
33129
33130 Roo.tree.TreeSorter.prototype = {
33131     doSort : function(node){
33132         node.sort(this.sortFn);
33133     },
33134     
33135     compareNodes : function(n1, n2){
33136         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33137     },
33138     
33139     updateSort : function(tree, node){
33140         if(node.childrenRendered){
33141             this.doSort.defer(1, this, [node]);
33142         }
33143     }
33144 };/*
33145  * Based on:
33146  * Ext JS Library 1.1.1
33147  * Copyright(c) 2006-2007, Ext JS, LLC.
33148  *
33149  * Originally Released Under LGPL - original licence link has changed is not relivant.
33150  *
33151  * Fork - LGPL
33152  * <script type="text/javascript">
33153  */
33154
33155 if(Roo.dd.DropZone){
33156     
33157 Roo.tree.TreeDropZone = function(tree, config){
33158     this.allowParentInsert = false;
33159     this.allowContainerDrop = false;
33160     this.appendOnly = false;
33161     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33162     this.tree = tree;
33163     this.lastInsertClass = "x-tree-no-status";
33164     this.dragOverData = {};
33165 };
33166
33167 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33168     ddGroup : "TreeDD",
33169     
33170     expandDelay : 1000,
33171     
33172     expandNode : function(node){
33173         if(node.hasChildNodes() && !node.isExpanded()){
33174             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33175         }
33176     },
33177     
33178     queueExpand : function(node){
33179         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33180     },
33181     
33182     cancelExpand : function(){
33183         if(this.expandProcId){
33184             clearTimeout(this.expandProcId);
33185             this.expandProcId = false;
33186         }
33187     },
33188     
33189     isValidDropPoint : function(n, pt, dd, e, data){
33190         if(!n || !data){ return false; }
33191         var targetNode = n.node;
33192         var dropNode = data.node;
33193         // default drop rules
33194         if(!(targetNode && targetNode.isTarget && pt)){
33195             return false;
33196         }
33197         if(pt == "append" && targetNode.allowChildren === false){
33198             return false;
33199         }
33200         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33201             return false;
33202         }
33203         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33204             return false;
33205         }
33206         // reuse the object
33207         var overEvent = this.dragOverData;
33208         overEvent.tree = this.tree;
33209         overEvent.target = targetNode;
33210         overEvent.data = data;
33211         overEvent.point = pt;
33212         overEvent.source = dd;
33213         overEvent.rawEvent = e;
33214         overEvent.dropNode = dropNode;
33215         overEvent.cancel = false;  
33216         var result = this.tree.fireEvent("nodedragover", overEvent);
33217         return overEvent.cancel === false && result !== false;
33218     },
33219     
33220     getDropPoint : function(e, n, dd){
33221         var tn = n.node;
33222         if(tn.isRoot){
33223             return tn.allowChildren !== false ? "append" : false; // always append for root
33224         }
33225         var dragEl = n.ddel;
33226         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33227         var y = Roo.lib.Event.getPageY(e);
33228         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33229         
33230         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33231         var noAppend = tn.allowChildren === false;
33232         if(this.appendOnly || tn.parentNode.allowChildren === false){
33233             return noAppend ? false : "append";
33234         }
33235         var noBelow = false;
33236         if(!this.allowParentInsert){
33237             noBelow = tn.hasChildNodes() && tn.isExpanded();
33238         }
33239         var q = (b - t) / (noAppend ? 2 : 3);
33240         if(y >= t && y < (t + q)){
33241             return "above";
33242         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33243             return "below";
33244         }else{
33245             return "append";
33246         }
33247     },
33248     
33249     onNodeEnter : function(n, dd, e, data){
33250         this.cancelExpand();
33251     },
33252     
33253     onNodeOver : function(n, dd, e, data){
33254         var pt = this.getDropPoint(e, n, dd);
33255         var node = n.node;
33256         
33257         // auto node expand check
33258         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33259             this.queueExpand(node);
33260         }else if(pt != "append"){
33261             this.cancelExpand();
33262         }
33263         
33264         // set the insert point style on the target node
33265         var returnCls = this.dropNotAllowed;
33266         if(this.isValidDropPoint(n, pt, dd, e, data)){
33267            if(pt){
33268                var el = n.ddel;
33269                var cls;
33270                if(pt == "above"){
33271                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33272                    cls = "x-tree-drag-insert-above";
33273                }else if(pt == "below"){
33274                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33275                    cls = "x-tree-drag-insert-below";
33276                }else{
33277                    returnCls = "x-tree-drop-ok-append";
33278                    cls = "x-tree-drag-append";
33279                }
33280                if(this.lastInsertClass != cls){
33281                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33282                    this.lastInsertClass = cls;
33283                }
33284            }
33285        }
33286        return returnCls;
33287     },
33288     
33289     onNodeOut : function(n, dd, e, data){
33290         this.cancelExpand();
33291         this.removeDropIndicators(n);
33292     },
33293     
33294     onNodeDrop : function(n, dd, e, data){
33295         var point = this.getDropPoint(e, n, dd);
33296         var targetNode = n.node;
33297         targetNode.ui.startDrop();
33298         if(!this.isValidDropPoint(n, point, dd, e, data)){
33299             targetNode.ui.endDrop();
33300             return false;
33301         }
33302         // first try to find the drop node
33303         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33304         var dropEvent = {
33305             tree : this.tree,
33306             target: targetNode,
33307             data: data,
33308             point: point,
33309             source: dd,
33310             rawEvent: e,
33311             dropNode: dropNode,
33312             cancel: !dropNode   
33313         };
33314         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33315         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33316             targetNode.ui.endDrop();
33317             return false;
33318         }
33319         // allow target changing
33320         targetNode = dropEvent.target;
33321         if(point == "append" && !targetNode.isExpanded()){
33322             targetNode.expand(false, null, function(){
33323                 this.completeDrop(dropEvent);
33324             }.createDelegate(this));
33325         }else{
33326             this.completeDrop(dropEvent);
33327         }
33328         return true;
33329     },
33330     
33331     completeDrop : function(de){
33332         var ns = de.dropNode, p = de.point, t = de.target;
33333         if(!(ns instanceof Array)){
33334             ns = [ns];
33335         }
33336         var n;
33337         for(var i = 0, len = ns.length; i < len; i++){
33338             n = ns[i];
33339             if(p == "above"){
33340                 t.parentNode.insertBefore(n, t);
33341             }else if(p == "below"){
33342                 t.parentNode.insertBefore(n, t.nextSibling);
33343             }else{
33344                 t.appendChild(n);
33345             }
33346         }
33347         n.ui.focus();
33348         if(this.tree.hlDrop){
33349             n.ui.highlight();
33350         }
33351         t.ui.endDrop();
33352         this.tree.fireEvent("nodedrop", de);
33353     },
33354     
33355     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33356         if(this.tree.hlDrop){
33357             dropNode.ui.focus();
33358             dropNode.ui.highlight();
33359         }
33360         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33361     },
33362     
33363     getTree : function(){
33364         return this.tree;
33365     },
33366     
33367     removeDropIndicators : function(n){
33368         if(n && n.ddel){
33369             var el = n.ddel;
33370             Roo.fly(el).removeClass([
33371                     "x-tree-drag-insert-above",
33372                     "x-tree-drag-insert-below",
33373                     "x-tree-drag-append"]);
33374             this.lastInsertClass = "_noclass";
33375         }
33376     },
33377     
33378     beforeDragDrop : function(target, e, id){
33379         this.cancelExpand();
33380         return true;
33381     },
33382     
33383     afterRepair : function(data){
33384         if(data && Roo.enableFx){
33385             data.node.ui.highlight();
33386         }
33387         this.hideProxy();
33388     }    
33389 });
33390
33391 }
33392 /*
33393  * Based on:
33394  * Ext JS Library 1.1.1
33395  * Copyright(c) 2006-2007, Ext JS, LLC.
33396  *
33397  * Originally Released Under LGPL - original licence link has changed is not relivant.
33398  *
33399  * Fork - LGPL
33400  * <script type="text/javascript">
33401  */
33402  
33403
33404 if(Roo.dd.DragZone){
33405 Roo.tree.TreeDragZone = function(tree, config){
33406     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33407     this.tree = tree;
33408 };
33409
33410 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33411     ddGroup : "TreeDD",
33412     
33413     onBeforeDrag : function(data, e){
33414         var n = data.node;
33415         return n && n.draggable && !n.disabled;
33416     },
33417     
33418     onInitDrag : function(e){
33419         var data = this.dragData;
33420         this.tree.getSelectionModel().select(data.node);
33421         this.proxy.update("");
33422         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33423         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33424     },
33425     
33426     getRepairXY : function(e, data){
33427         return data.node.ui.getDDRepairXY();
33428     },
33429     
33430     onEndDrag : function(data, e){
33431         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33432     },
33433     
33434     onValidDrop : function(dd, e, id){
33435         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33436         this.hideProxy();
33437     },
33438     
33439     beforeInvalidDrop : function(e, id){
33440         // this scrolls the original position back into view
33441         var sm = this.tree.getSelectionModel();
33442         sm.clearSelections();
33443         sm.select(this.dragData.node);
33444     }
33445 });
33446 }/*
33447  * Based on:
33448  * Ext JS Library 1.1.1
33449  * Copyright(c) 2006-2007, Ext JS, LLC.
33450  *
33451  * Originally Released Under LGPL - original licence link has changed is not relivant.
33452  *
33453  * Fork - LGPL
33454  * <script type="text/javascript">
33455  */
33456 /**
33457  * @class Roo.tree.TreeEditor
33458  * @extends Roo.Editor
33459  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33460  * as the editor field.
33461  * @constructor
33462  * @param {TreePanel} tree
33463  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33464  */
33465 Roo.tree.TreeEditor = function(tree, config){
33466     config = config || {};
33467     var field = config.events ? config : new Roo.form.TextField(config);
33468     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
33469
33470     this.tree = tree;
33471
33472     tree.on('beforeclick', this.beforeNodeClick, this);
33473     tree.getTreeEl().on('mousedown', this.hide, this);
33474     this.on('complete', this.updateNode, this);
33475     this.on('beforestartedit', this.fitToTree, this);
33476     this.on('startedit', this.bindScroll, this, {delay:10});
33477     this.on('specialkey', this.onSpecialKey, this);
33478 };
33479
33480 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33481     /**
33482      * @cfg {String} alignment
33483      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33484      */
33485     alignment: "l-l",
33486     // inherit
33487     autoSize: false,
33488     /**
33489      * @cfg {Boolean} hideEl
33490      * True to hide the bound element while the editor is displayed (defaults to false)
33491      */
33492     hideEl : false,
33493     /**
33494      * @cfg {String} cls
33495      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33496      */
33497     cls: "x-small-editor x-tree-editor",
33498     /**
33499      * @cfg {Boolean} shim
33500      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33501      */
33502     shim:false,
33503     // inherit
33504     shadow:"frame",
33505     /**
33506      * @cfg {Number} maxWidth
33507      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33508      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33509      * scroll and client offsets into account prior to each edit.
33510      */
33511     maxWidth: 250,
33512
33513     editDelay : 350,
33514
33515     // private
33516     fitToTree : function(ed, el){
33517         var td = this.tree.getTreeEl().dom, nd = el.dom;
33518         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33519             td.scrollLeft = nd.offsetLeft;
33520         }
33521         var w = Math.min(
33522                 this.maxWidth,
33523                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33524         this.setSize(w, '');
33525     },
33526
33527     // private
33528     triggerEdit : function(node){
33529         this.completeEdit();
33530         this.editNode = node;
33531         this.startEdit(node.ui.textNode, node.text);
33532     },
33533
33534     // private
33535     bindScroll : function(){
33536         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33537     },
33538
33539     // private
33540     beforeNodeClick : function(node, e){
33541         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33542         this.lastClick = new Date();
33543         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33544             e.stopEvent();
33545             this.triggerEdit(node);
33546             return false;
33547         }
33548     },
33549
33550     // private
33551     updateNode : function(ed, value){
33552         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33553         this.editNode.setText(value);
33554     },
33555
33556     // private
33557     onHide : function(){
33558         Roo.tree.TreeEditor.superclass.onHide.call(this);
33559         if(this.editNode){
33560             this.editNode.ui.focus();
33561         }
33562     },
33563
33564     // private
33565     onSpecialKey : function(field, e){
33566         var k = e.getKey();
33567         if(k == e.ESC){
33568             e.stopEvent();
33569             this.cancelEdit();
33570         }else if(k == e.ENTER && !e.hasModifier()){
33571             e.stopEvent();
33572             this.completeEdit();
33573         }
33574     }
33575 });//<Script type="text/javascript">
33576 /*
33577  * Based on:
33578  * Ext JS Library 1.1.1
33579  * Copyright(c) 2006-2007, Ext JS, LLC.
33580  *
33581  * Originally Released Under LGPL - original licence link has changed is not relivant.
33582  *
33583  * Fork - LGPL
33584  * <script type="text/javascript">
33585  */
33586  
33587 /**
33588  * Not documented??? - probably should be...
33589  */
33590
33591 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33592     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33593     
33594     renderElements : function(n, a, targetNode, bulkRender){
33595         //consel.log("renderElements?");
33596         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33597
33598         var t = n.getOwnerTree();
33599         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33600         
33601         var cols = t.columns;
33602         var bw = t.borderWidth;
33603         var c = cols[0];
33604         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33605          var cb = typeof a.checked == "boolean";
33606         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33607         var colcls = 'x-t-' + tid + '-c0';
33608         var buf = [
33609             '<li class="x-tree-node">',
33610             
33611                 
33612                 '<div class="x-tree-node-el ', a.cls,'">',
33613                     // extran...
33614                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33615                 
33616                 
33617                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33618                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33619                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33620                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33621                            (a.iconCls ? ' '+a.iconCls : ''),
33622                            '" unselectable="on" />',
33623                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33624                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33625                              
33626                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33627                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33628                             '<span unselectable="on" qtip="' + tx + '">',
33629                              tx,
33630                              '</span></a>' ,
33631                     '</div>',
33632                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33633                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33634                  ];
33635         for(var i = 1, len = cols.length; i < len; i++){
33636             c = cols[i];
33637             colcls = 'x-t-' + tid + '-c' +i;
33638             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33639             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33640                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33641                       "</div>");
33642          }
33643          
33644          buf.push(
33645             '</a>',
33646             '<div class="x-clear"></div></div>',
33647             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33648             "</li>");
33649         
33650         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33651             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33652                                 n.nextSibling.ui.getEl(), buf.join(""));
33653         }else{
33654             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33655         }
33656         var el = this.wrap.firstChild;
33657         this.elRow = el;
33658         this.elNode = el.firstChild;
33659         this.ranchor = el.childNodes[1];
33660         this.ctNode = this.wrap.childNodes[1];
33661         var cs = el.firstChild.childNodes;
33662         this.indentNode = cs[0];
33663         this.ecNode = cs[1];
33664         this.iconNode = cs[2];
33665         var index = 3;
33666         if(cb){
33667             this.checkbox = cs[3];
33668             index++;
33669         }
33670         this.anchor = cs[index];
33671         
33672         this.textNode = cs[index].firstChild;
33673         
33674         //el.on("click", this.onClick, this);
33675         //el.on("dblclick", this.onDblClick, this);
33676         
33677         
33678        // console.log(this);
33679     },
33680     initEvents : function(){
33681         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33682         
33683             
33684         var a = this.ranchor;
33685
33686         var el = Roo.get(a);
33687
33688         if(Roo.isOpera){ // opera render bug ignores the CSS
33689             el.setStyle("text-decoration", "none");
33690         }
33691
33692         el.on("click", this.onClick, this);
33693         el.on("dblclick", this.onDblClick, this);
33694         el.on("contextmenu", this.onContextMenu, this);
33695         
33696     },
33697     
33698     /*onSelectedChange : function(state){
33699         if(state){
33700             this.focus();
33701             this.addClass("x-tree-selected");
33702         }else{
33703             //this.blur();
33704             this.removeClass("x-tree-selected");
33705         }
33706     },*/
33707     addClass : function(cls){
33708         if(this.elRow){
33709             Roo.fly(this.elRow).addClass(cls);
33710         }
33711         
33712     },
33713     
33714     
33715     removeClass : function(cls){
33716         if(this.elRow){
33717             Roo.fly(this.elRow).removeClass(cls);
33718         }
33719     }
33720
33721     
33722     
33723 });//<Script type="text/javascript">
33724
33725 /*
33726  * Based on:
33727  * Ext JS Library 1.1.1
33728  * Copyright(c) 2006-2007, Ext JS, LLC.
33729  *
33730  * Originally Released Under LGPL - original licence link has changed is not relivant.
33731  *
33732  * Fork - LGPL
33733  * <script type="text/javascript">
33734  */
33735  
33736
33737 /**
33738  * @class Roo.tree.ColumnTree
33739  * @extends Roo.data.TreePanel
33740  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33741  * @cfg {int} borderWidth  compined right/left border allowance
33742  * @constructor
33743  * @param {String/HTMLElement/Element} el The container element
33744  * @param {Object} config
33745  */
33746 Roo.tree.ColumnTree =  function(el, config)
33747 {
33748    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33749    this.addEvents({
33750         /**
33751         * @event resize
33752         * Fire this event on a container when it resizes
33753         * @param {int} w Width
33754         * @param {int} h Height
33755         */
33756        "resize" : true
33757     });
33758     this.on('resize', this.onResize, this);
33759 };
33760
33761 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33762     //lines:false,
33763     
33764     
33765     borderWidth: Roo.isBorderBox ? 0 : 2, 
33766     headEls : false,
33767     
33768     render : function(){
33769         // add the header.....
33770        
33771         Roo.tree.ColumnTree.superclass.render.apply(this);
33772         
33773         this.el.addClass('x-column-tree');
33774         
33775         this.headers = this.el.createChild(
33776             {cls:'x-tree-headers'},this.innerCt.dom);
33777    
33778         var cols = this.columns, c;
33779         var totalWidth = 0;
33780         this.headEls = [];
33781         var  len = cols.length;
33782         for(var i = 0; i < len; i++){
33783              c = cols[i];
33784              totalWidth += c.width;
33785             this.headEls.push(this.headers.createChild({
33786                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33787                  cn: {
33788                      cls:'x-tree-hd-text',
33789                      html: c.header
33790                  },
33791                  style:'width:'+(c.width-this.borderWidth)+'px;'
33792              }));
33793         }
33794         this.headers.createChild({cls:'x-clear'});
33795         // prevent floats from wrapping when clipped
33796         this.headers.setWidth(totalWidth);
33797         //this.innerCt.setWidth(totalWidth);
33798         this.innerCt.setStyle({ overflow: 'auto' });
33799         this.onResize(this.width, this.height);
33800              
33801         
33802     },
33803     onResize : function(w,h)
33804     {
33805         this.height = h;
33806         this.width = w;
33807         // resize cols..
33808         this.innerCt.setWidth(this.width);
33809         this.innerCt.setHeight(this.height-20);
33810         
33811         // headers...
33812         var cols = this.columns, c;
33813         var totalWidth = 0;
33814         var expEl = false;
33815         var len = cols.length;
33816         for(var i = 0; i < len; i++){
33817             c = cols[i];
33818             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33819                 // it's the expander..
33820                 expEl  = this.headEls[i];
33821                 continue;
33822             }
33823             totalWidth += c.width;
33824             
33825         }
33826         if (expEl) {
33827             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33828         }
33829         this.headers.setWidth(w-20);
33830
33831         
33832         
33833         
33834     }
33835 });
33836 /*
33837  * Based on:
33838  * Ext JS Library 1.1.1
33839  * Copyright(c) 2006-2007, Ext JS, LLC.
33840  *
33841  * Originally Released Under LGPL - original licence link has changed is not relivant.
33842  *
33843  * Fork - LGPL
33844  * <script type="text/javascript">
33845  */
33846  
33847 /**
33848  * @class Roo.menu.Menu
33849  * @extends Roo.util.Observable
33850  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33851  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33852  * @constructor
33853  * Creates a new Menu
33854  * @param {Object} config Configuration options
33855  */
33856 Roo.menu.Menu = function(config){
33857     Roo.apply(this, config);
33858     this.id = this.id || Roo.id();
33859     this.addEvents({
33860         /**
33861          * @event beforeshow
33862          * Fires before this menu is displayed
33863          * @param {Roo.menu.Menu} this
33864          */
33865         beforeshow : true,
33866         /**
33867          * @event beforehide
33868          * Fires before this menu is hidden
33869          * @param {Roo.menu.Menu} this
33870          */
33871         beforehide : true,
33872         /**
33873          * @event show
33874          * Fires after this menu is displayed
33875          * @param {Roo.menu.Menu} this
33876          */
33877         show : true,
33878         /**
33879          * @event hide
33880          * Fires after this menu is hidden
33881          * @param {Roo.menu.Menu} this
33882          */
33883         hide : true,
33884         /**
33885          * @event click
33886          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33887          * @param {Roo.menu.Menu} this
33888          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33889          * @param {Roo.EventObject} e
33890          */
33891         click : true,
33892         /**
33893          * @event mouseover
33894          * Fires when the mouse is hovering over this menu
33895          * @param {Roo.menu.Menu} this
33896          * @param {Roo.EventObject} e
33897          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33898          */
33899         mouseover : true,
33900         /**
33901          * @event mouseout
33902          * Fires when the mouse exits this menu
33903          * @param {Roo.menu.Menu} this
33904          * @param {Roo.EventObject} e
33905          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33906          */
33907         mouseout : true,
33908         /**
33909          * @event itemclick
33910          * Fires when a menu item contained in this menu is clicked
33911          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33912          * @param {Roo.EventObject} e
33913          */
33914         itemclick: true
33915     });
33916     if (this.registerMenu) {
33917         Roo.menu.MenuMgr.register(this);
33918     }
33919     
33920     var mis = this.items;
33921     this.items = new Roo.util.MixedCollection();
33922     if(mis){
33923         this.add.apply(this, mis);
33924     }
33925 };
33926
33927 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33928     /**
33929      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33930      */
33931     minWidth : 120,
33932     /**
33933      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33934      * for bottom-right shadow (defaults to "sides")
33935      */
33936     shadow : "sides",
33937     /**
33938      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33939      * this menu (defaults to "tl-tr?")
33940      */
33941     subMenuAlign : "tl-tr?",
33942     /**
33943      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33944      * relative to its element of origin (defaults to "tl-bl?")
33945      */
33946     defaultAlign : "tl-bl?",
33947     /**
33948      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33949      */
33950     allowOtherMenus : false,
33951     /**
33952      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33953      */
33954     registerMenu : true,
33955
33956     hidden:true,
33957
33958     // private
33959     render : function(){
33960         if(this.el){
33961             return;
33962         }
33963         var el = this.el = new Roo.Layer({
33964             cls: "x-menu",
33965             shadow:this.shadow,
33966             constrain: false,
33967             parentEl: this.parentEl || document.body,
33968             zindex:15000
33969         });
33970
33971         this.keyNav = new Roo.menu.MenuNav(this);
33972
33973         if(this.plain){
33974             el.addClass("x-menu-plain");
33975         }
33976         if(this.cls){
33977             el.addClass(this.cls);
33978         }
33979         // generic focus element
33980         this.focusEl = el.createChild({
33981             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
33982         });
33983         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
33984         ul.on("click", this.onClick, this);
33985         ul.on("mouseover", this.onMouseOver, this);
33986         ul.on("mouseout", this.onMouseOut, this);
33987         this.items.each(function(item){
33988             var li = document.createElement("li");
33989             li.className = "x-menu-list-item";
33990             ul.dom.appendChild(li);
33991             item.render(li, this);
33992         }, this);
33993         this.ul = ul;
33994         this.autoWidth();
33995     },
33996
33997     // private
33998     autoWidth : function(){
33999         var el = this.el, ul = this.ul;
34000         if(!el){
34001             return;
34002         }
34003         var w = this.width;
34004         if(w){
34005             el.setWidth(w);
34006         }else if(Roo.isIE){
34007             el.setWidth(this.minWidth);
34008             var t = el.dom.offsetWidth; // force recalc
34009             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34010         }
34011     },
34012
34013     // private
34014     delayAutoWidth : function(){
34015         if(this.rendered){
34016             if(!this.awTask){
34017                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34018             }
34019             this.awTask.delay(20);
34020         }
34021     },
34022
34023     // private
34024     findTargetItem : function(e){
34025         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34026         if(t && t.menuItemId){
34027             return this.items.get(t.menuItemId);
34028         }
34029     },
34030
34031     // private
34032     onClick : function(e){
34033         var t;
34034         if(t = this.findTargetItem(e)){
34035             t.onClick(e);
34036             this.fireEvent("click", this, t, e);
34037         }
34038     },
34039
34040     // private
34041     setActiveItem : function(item, autoExpand){
34042         if(item != this.activeItem){
34043             if(this.activeItem){
34044                 this.activeItem.deactivate();
34045             }
34046             this.activeItem = item;
34047             item.activate(autoExpand);
34048         }else if(autoExpand){
34049             item.expandMenu();
34050         }
34051     },
34052
34053     // private
34054     tryActivate : function(start, step){
34055         var items = this.items;
34056         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34057             var item = items.get(i);
34058             if(!item.disabled && item.canActivate){
34059                 this.setActiveItem(item, false);
34060                 return item;
34061             }
34062         }
34063         return false;
34064     },
34065
34066     // private
34067     onMouseOver : function(e){
34068         var t;
34069         if(t = this.findTargetItem(e)){
34070             if(t.canActivate && !t.disabled){
34071                 this.setActiveItem(t, true);
34072             }
34073         }
34074         this.fireEvent("mouseover", this, e, t);
34075     },
34076
34077     // private
34078     onMouseOut : function(e){
34079         var t;
34080         if(t = this.findTargetItem(e)){
34081             if(t == this.activeItem && t.shouldDeactivate(e)){
34082                 this.activeItem.deactivate();
34083                 delete this.activeItem;
34084             }
34085         }
34086         this.fireEvent("mouseout", this, e, t);
34087     },
34088
34089     /**
34090      * Read-only.  Returns true if the menu is currently displayed, else false.
34091      * @type Boolean
34092      */
34093     isVisible : function(){
34094         return this.el && !this.hidden;
34095     },
34096
34097     /**
34098      * Displays this menu relative to another element
34099      * @param {String/HTMLElement/Roo.Element} element The element to align to
34100      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34101      * the element (defaults to this.defaultAlign)
34102      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34103      */
34104     show : function(el, pos, parentMenu){
34105         this.parentMenu = parentMenu;
34106         if(!this.el){
34107             this.render();
34108         }
34109         this.fireEvent("beforeshow", this);
34110         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34111     },
34112
34113     /**
34114      * Displays this menu at a specific xy position
34115      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34116      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34117      */
34118     showAt : function(xy, parentMenu, /* private: */_e){
34119         this.parentMenu = parentMenu;
34120         if(!this.el){
34121             this.render();
34122         }
34123         if(_e !== false){
34124             this.fireEvent("beforeshow", this);
34125             xy = this.el.adjustForConstraints(xy);
34126         }
34127         this.el.setXY(xy);
34128         this.el.show();
34129         this.hidden = false;
34130         this.focus();
34131         this.fireEvent("show", this);
34132     },
34133
34134     focus : function(){
34135         if(!this.hidden){
34136             this.doFocus.defer(50, this);
34137         }
34138     },
34139
34140     doFocus : function(){
34141         if(!this.hidden){
34142             this.focusEl.focus();
34143         }
34144     },
34145
34146     /**
34147      * Hides this menu and optionally all parent menus
34148      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34149      */
34150     hide : function(deep){
34151         if(this.el && this.isVisible()){
34152             this.fireEvent("beforehide", this);
34153             if(this.activeItem){
34154                 this.activeItem.deactivate();
34155                 this.activeItem = null;
34156             }
34157             this.el.hide();
34158             this.hidden = true;
34159             this.fireEvent("hide", this);
34160         }
34161         if(deep === true && this.parentMenu){
34162             this.parentMenu.hide(true);
34163         }
34164     },
34165
34166     /**
34167      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34168      * Any of the following are valid:
34169      * <ul>
34170      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34171      * <li>An HTMLElement object which will be converted to a menu item</li>
34172      * <li>A menu item config object that will be created as a new menu item</li>
34173      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34174      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34175      * </ul>
34176      * Usage:
34177      * <pre><code>
34178 // Create the menu
34179 var menu = new Roo.menu.Menu();
34180
34181 // Create a menu item to add by reference
34182 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34183
34184 // Add a bunch of items at once using different methods.
34185 // Only the last item added will be returned.
34186 var item = menu.add(
34187     menuItem,                // add existing item by ref
34188     'Dynamic Item',          // new TextItem
34189     '-',                     // new separator
34190     { text: 'Config Item' }  // new item by config
34191 );
34192 </code></pre>
34193      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34194      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34195      */
34196     add : function(){
34197         var a = arguments, l = a.length, item;
34198         for(var i = 0; i < l; i++){
34199             var el = a[i];
34200             if ((typeof(el) == "object") && el.xtype && el.xns) {
34201                 el = Roo.factory(el, Roo.menu);
34202             }
34203             
34204             if(el.render){ // some kind of Item
34205                 item = this.addItem(el);
34206             }else if(typeof el == "string"){ // string
34207                 if(el == "separator" || el == "-"){
34208                     item = this.addSeparator();
34209                 }else{
34210                     item = this.addText(el);
34211                 }
34212             }else if(el.tagName || el.el){ // element
34213                 item = this.addElement(el);
34214             }else if(typeof el == "object"){ // must be menu item config?
34215                 item = this.addMenuItem(el);
34216             }
34217         }
34218         return item;
34219     },
34220
34221     /**
34222      * Returns this menu's underlying {@link Roo.Element} object
34223      * @return {Roo.Element} The element
34224      */
34225     getEl : function(){
34226         if(!this.el){
34227             this.render();
34228         }
34229         return this.el;
34230     },
34231
34232     /**
34233      * Adds a separator bar to the menu
34234      * @return {Roo.menu.Item} The menu item that was added
34235      */
34236     addSeparator : function(){
34237         return this.addItem(new Roo.menu.Separator());
34238     },
34239
34240     /**
34241      * Adds an {@link Roo.Element} object to the menu
34242      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34243      * @return {Roo.menu.Item} The menu item that was added
34244      */
34245     addElement : function(el){
34246         return this.addItem(new Roo.menu.BaseItem(el));
34247     },
34248
34249     /**
34250      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34251      * @param {Roo.menu.Item} item The menu item to add
34252      * @return {Roo.menu.Item} The menu item that was added
34253      */
34254     addItem : function(item){
34255         this.items.add(item);
34256         if(this.ul){
34257             var li = document.createElement("li");
34258             li.className = "x-menu-list-item";
34259             this.ul.dom.appendChild(li);
34260             item.render(li, this);
34261             this.delayAutoWidth();
34262         }
34263         return item;
34264     },
34265
34266     /**
34267      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34268      * @param {Object} config A MenuItem config object
34269      * @return {Roo.menu.Item} The menu item that was added
34270      */
34271     addMenuItem : function(config){
34272         if(!(config instanceof Roo.menu.Item)){
34273             if(typeof config.checked == "boolean"){ // must be check menu item config?
34274                 config = new Roo.menu.CheckItem(config);
34275             }else{
34276                 config = new Roo.menu.Item(config);
34277             }
34278         }
34279         return this.addItem(config);
34280     },
34281
34282     /**
34283      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34284      * @param {String} text The text to display in the menu item
34285      * @return {Roo.menu.Item} The menu item that was added
34286      */
34287     addText : function(text){
34288         return this.addItem(new Roo.menu.TextItem({ text : text }));
34289     },
34290
34291     /**
34292      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34293      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34294      * @param {Roo.menu.Item} item The menu item to add
34295      * @return {Roo.menu.Item} The menu item that was added
34296      */
34297     insert : function(index, item){
34298         this.items.insert(index, item);
34299         if(this.ul){
34300             var li = document.createElement("li");
34301             li.className = "x-menu-list-item";
34302             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34303             item.render(li, this);
34304             this.delayAutoWidth();
34305         }
34306         return item;
34307     },
34308
34309     /**
34310      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34311      * @param {Roo.menu.Item} item The menu item to remove
34312      */
34313     remove : function(item){
34314         this.items.removeKey(item.id);
34315         item.destroy();
34316     },
34317
34318     /**
34319      * Removes and destroys all items in the menu
34320      */
34321     removeAll : function(){
34322         var f;
34323         while(f = this.items.first()){
34324             this.remove(f);
34325         }
34326     }
34327 });
34328
34329 // MenuNav is a private utility class used internally by the Menu
34330 Roo.menu.MenuNav = function(menu){
34331     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34332     this.scope = this.menu = menu;
34333 };
34334
34335 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34336     doRelay : function(e, h){
34337         var k = e.getKey();
34338         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34339             this.menu.tryActivate(0, 1);
34340             return false;
34341         }
34342         return h.call(this.scope || this, e, this.menu);
34343     },
34344
34345     up : function(e, m){
34346         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34347             m.tryActivate(m.items.length-1, -1);
34348         }
34349     },
34350
34351     down : function(e, m){
34352         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34353             m.tryActivate(0, 1);
34354         }
34355     },
34356
34357     right : function(e, m){
34358         if(m.activeItem){
34359             m.activeItem.expandMenu(true);
34360         }
34361     },
34362
34363     left : function(e, m){
34364         m.hide();
34365         if(m.parentMenu && m.parentMenu.activeItem){
34366             m.parentMenu.activeItem.activate();
34367         }
34368     },
34369
34370     enter : function(e, m){
34371         if(m.activeItem){
34372             e.stopPropagation();
34373             m.activeItem.onClick(e);
34374             m.fireEvent("click", this, m.activeItem);
34375             return true;
34376         }
34377     }
34378 });/*
34379  * Based on:
34380  * Ext JS Library 1.1.1
34381  * Copyright(c) 2006-2007, Ext JS, LLC.
34382  *
34383  * Originally Released Under LGPL - original licence link has changed is not relivant.
34384  *
34385  * Fork - LGPL
34386  * <script type="text/javascript">
34387  */
34388  
34389 /**
34390  * @class Roo.menu.MenuMgr
34391  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34392  * @singleton
34393  */
34394 Roo.menu.MenuMgr = function(){
34395    var menus, active, groups = {}, attached = false, lastShow = new Date();
34396
34397    // private - called when first menu is created
34398    function init(){
34399        menus = {};
34400        active = new Roo.util.MixedCollection();
34401        Roo.get(document).addKeyListener(27, function(){
34402            if(active.length > 0){
34403                hideAll();
34404            }
34405        });
34406    }
34407
34408    // private
34409    function hideAll(){
34410        if(active && active.length > 0){
34411            var c = active.clone();
34412            c.each(function(m){
34413                m.hide();
34414            });
34415        }
34416    }
34417
34418    // private
34419    function onHide(m){
34420        active.remove(m);
34421        if(active.length < 1){
34422            Roo.get(document).un("mousedown", onMouseDown);
34423            attached = false;
34424        }
34425    }
34426
34427    // private
34428    function onShow(m){
34429        var last = active.last();
34430        lastShow = new Date();
34431        active.add(m);
34432        if(!attached){
34433            Roo.get(document).on("mousedown", onMouseDown);
34434            attached = true;
34435        }
34436        if(m.parentMenu){
34437           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34438           m.parentMenu.activeChild = m;
34439        }else if(last && last.isVisible()){
34440           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34441        }
34442    }
34443
34444    // private
34445    function onBeforeHide(m){
34446        if(m.activeChild){
34447            m.activeChild.hide();
34448        }
34449        if(m.autoHideTimer){
34450            clearTimeout(m.autoHideTimer);
34451            delete m.autoHideTimer;
34452        }
34453    }
34454
34455    // private
34456    function onBeforeShow(m){
34457        var pm = m.parentMenu;
34458        if(!pm && !m.allowOtherMenus){
34459            hideAll();
34460        }else if(pm && pm.activeChild && active != m){
34461            pm.activeChild.hide();
34462        }
34463    }
34464
34465    // private
34466    function onMouseDown(e){
34467        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34468            hideAll();
34469        }
34470    }
34471
34472    // private
34473    function onBeforeCheck(mi, state){
34474        if(state){
34475            var g = groups[mi.group];
34476            for(var i = 0, l = g.length; i < l; i++){
34477                if(g[i] != mi){
34478                    g[i].setChecked(false);
34479                }
34480            }
34481        }
34482    }
34483
34484    return {
34485
34486        /**
34487         * Hides all menus that are currently visible
34488         */
34489        hideAll : function(){
34490             hideAll();  
34491        },
34492
34493        // private
34494        register : function(menu){
34495            if(!menus){
34496                init();
34497            }
34498            menus[menu.id] = menu;
34499            menu.on("beforehide", onBeforeHide);
34500            menu.on("hide", onHide);
34501            menu.on("beforeshow", onBeforeShow);
34502            menu.on("show", onShow);
34503            var g = menu.group;
34504            if(g && menu.events["checkchange"]){
34505                if(!groups[g]){
34506                    groups[g] = [];
34507                }
34508                groups[g].push(menu);
34509                menu.on("checkchange", onCheck);
34510            }
34511        },
34512
34513         /**
34514          * Returns a {@link Roo.menu.Menu} object
34515          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34516          * be used to generate and return a new Menu instance.
34517          */
34518        get : function(menu){
34519            if(typeof menu == "string"){ // menu id
34520                return menus[menu];
34521            }else if(menu.events){  // menu instance
34522                return menu;
34523            }else if(typeof menu.length == 'number'){ // array of menu items?
34524                return new Roo.menu.Menu({items:menu});
34525            }else{ // otherwise, must be a config
34526                return new Roo.menu.Menu(menu);
34527            }
34528        },
34529
34530        // private
34531        unregister : function(menu){
34532            delete menus[menu.id];
34533            menu.un("beforehide", onBeforeHide);
34534            menu.un("hide", onHide);
34535            menu.un("beforeshow", onBeforeShow);
34536            menu.un("show", onShow);
34537            var g = menu.group;
34538            if(g && menu.events["checkchange"]){
34539                groups[g].remove(menu);
34540                menu.un("checkchange", onCheck);
34541            }
34542        },
34543
34544        // private
34545        registerCheckable : function(menuItem){
34546            var g = menuItem.group;
34547            if(g){
34548                if(!groups[g]){
34549                    groups[g] = [];
34550                }
34551                groups[g].push(menuItem);
34552                menuItem.on("beforecheckchange", onBeforeCheck);
34553            }
34554        },
34555
34556        // private
34557        unregisterCheckable : function(menuItem){
34558            var g = menuItem.group;
34559            if(g){
34560                groups[g].remove(menuItem);
34561                menuItem.un("beforecheckchange", onBeforeCheck);
34562            }
34563        }
34564    };
34565 }();/*
34566  * Based on:
34567  * Ext JS Library 1.1.1
34568  * Copyright(c) 2006-2007, Ext JS, LLC.
34569  *
34570  * Originally Released Under LGPL - original licence link has changed is not relivant.
34571  *
34572  * Fork - LGPL
34573  * <script type="text/javascript">
34574  */
34575  
34576
34577 /**
34578  * @class Roo.menu.BaseItem
34579  * @extends Roo.Component
34580  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34581  * management and base configuration options shared by all menu components.
34582  * @constructor
34583  * Creates a new BaseItem
34584  * @param {Object} config Configuration options
34585  */
34586 Roo.menu.BaseItem = function(config){
34587     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34588
34589     this.addEvents({
34590         /**
34591          * @event click
34592          * Fires when this item is clicked
34593          * @param {Roo.menu.BaseItem} this
34594          * @param {Roo.EventObject} e
34595          */
34596         click: true,
34597         /**
34598          * @event activate
34599          * Fires when this item is activated
34600          * @param {Roo.menu.BaseItem} this
34601          */
34602         activate : true,
34603         /**
34604          * @event deactivate
34605          * Fires when this item is deactivated
34606          * @param {Roo.menu.BaseItem} this
34607          */
34608         deactivate : true
34609     });
34610
34611     if(this.handler){
34612         this.on("click", this.handler, this.scope, true);
34613     }
34614 };
34615
34616 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34617     /**
34618      * @cfg {Function} handler
34619      * A function that will handle the click event of this menu item (defaults to undefined)
34620      */
34621     /**
34622      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34623      */
34624     canActivate : false,
34625     /**
34626      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34627      */
34628     activeClass : "x-menu-item-active",
34629     /**
34630      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34631      */
34632     hideOnClick : true,
34633     /**
34634      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34635      */
34636     hideDelay : 100,
34637
34638     // private
34639     ctype: "Roo.menu.BaseItem",
34640
34641     // private
34642     actionMode : "container",
34643
34644     // private
34645     render : function(container, parentMenu){
34646         this.parentMenu = parentMenu;
34647         Roo.menu.BaseItem.superclass.render.call(this, container);
34648         this.container.menuItemId = this.id;
34649     },
34650
34651     // private
34652     onRender : function(container, position){
34653         this.el = Roo.get(this.el);
34654         container.dom.appendChild(this.el.dom);
34655     },
34656
34657     // private
34658     onClick : function(e){
34659         if(!this.disabled && this.fireEvent("click", this, e) !== false
34660                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34661             this.handleClick(e);
34662         }else{
34663             e.stopEvent();
34664         }
34665     },
34666
34667     // private
34668     activate : function(){
34669         if(this.disabled){
34670             return false;
34671         }
34672         var li = this.container;
34673         li.addClass(this.activeClass);
34674         this.region = li.getRegion().adjust(2, 2, -2, -2);
34675         this.fireEvent("activate", this);
34676         return true;
34677     },
34678
34679     // private
34680     deactivate : function(){
34681         this.container.removeClass(this.activeClass);
34682         this.fireEvent("deactivate", this);
34683     },
34684
34685     // private
34686     shouldDeactivate : function(e){
34687         return !this.region || !this.region.contains(e.getPoint());
34688     },
34689
34690     // private
34691     handleClick : function(e){
34692         if(this.hideOnClick){
34693             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34694         }
34695     },
34696
34697     // private
34698     expandMenu : function(autoActivate){
34699         // do nothing
34700     },
34701
34702     // private
34703     hideMenu : function(){
34704         // do nothing
34705     }
34706 });/*
34707  * Based on:
34708  * Ext JS Library 1.1.1
34709  * Copyright(c) 2006-2007, Ext JS, LLC.
34710  *
34711  * Originally Released Under LGPL - original licence link has changed is not relivant.
34712  *
34713  * Fork - LGPL
34714  * <script type="text/javascript">
34715  */
34716  
34717 /**
34718  * @class Roo.menu.Adapter
34719  * @extends Roo.menu.BaseItem
34720  * 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.
34721  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34722  * @constructor
34723  * Creates a new Adapter
34724  * @param {Object} config Configuration options
34725  */
34726 Roo.menu.Adapter = function(component, config){
34727     Roo.menu.Adapter.superclass.constructor.call(this, config);
34728     this.component = component;
34729 };
34730 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34731     // private
34732     canActivate : true,
34733
34734     // private
34735     onRender : function(container, position){
34736         this.component.render(container);
34737         this.el = this.component.getEl();
34738     },
34739
34740     // private
34741     activate : function(){
34742         if(this.disabled){
34743             return false;
34744         }
34745         this.component.focus();
34746         this.fireEvent("activate", this);
34747         return true;
34748     },
34749
34750     // private
34751     deactivate : function(){
34752         this.fireEvent("deactivate", this);
34753     },
34754
34755     // private
34756     disable : function(){
34757         this.component.disable();
34758         Roo.menu.Adapter.superclass.disable.call(this);
34759     },
34760
34761     // private
34762     enable : function(){
34763         this.component.enable();
34764         Roo.menu.Adapter.superclass.enable.call(this);
34765     }
34766 });/*
34767  * Based on:
34768  * Ext JS Library 1.1.1
34769  * Copyright(c) 2006-2007, Ext JS, LLC.
34770  *
34771  * Originally Released Under LGPL - original licence link has changed is not relivant.
34772  *
34773  * Fork - LGPL
34774  * <script type="text/javascript">
34775  */
34776
34777 /**
34778  * @class Roo.menu.TextItem
34779  * @extends Roo.menu.BaseItem
34780  * Adds a static text string to a menu, usually used as either a heading or group separator.
34781  * Note: old style constructor with text is still supported.
34782  * 
34783  * @constructor
34784  * Creates a new TextItem
34785  * @param {Object} cfg Configuration
34786  */
34787 Roo.menu.TextItem = function(cfg){
34788     if (typeof(cfg) == 'string') {
34789         this.text = cfg;
34790     } else {
34791         Roo.apply(this,cfg);
34792     }
34793     
34794     Roo.menu.TextItem.superclass.constructor.call(this);
34795 };
34796
34797 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34798     /**
34799      * @cfg {Boolean} text Text to show on item.
34800      */
34801     text : '',
34802     
34803     /**
34804      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34805      */
34806     hideOnClick : false,
34807     /**
34808      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34809      */
34810     itemCls : "x-menu-text",
34811
34812     // private
34813     onRender : function(){
34814         var s = document.createElement("span");
34815         s.className = this.itemCls;
34816         s.innerHTML = this.text;
34817         this.el = s;
34818         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34819     }
34820 });/*
34821  * Based on:
34822  * Ext JS Library 1.1.1
34823  * Copyright(c) 2006-2007, Ext JS, LLC.
34824  *
34825  * Originally Released Under LGPL - original licence link has changed is not relivant.
34826  *
34827  * Fork - LGPL
34828  * <script type="text/javascript">
34829  */
34830
34831 /**
34832  * @class Roo.menu.Separator
34833  * @extends Roo.menu.BaseItem
34834  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34835  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34836  * @constructor
34837  * @param {Object} config Configuration options
34838  */
34839 Roo.menu.Separator = function(config){
34840     Roo.menu.Separator.superclass.constructor.call(this, config);
34841 };
34842
34843 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34844     /**
34845      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34846      */
34847     itemCls : "x-menu-sep",
34848     /**
34849      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34850      */
34851     hideOnClick : false,
34852
34853     // private
34854     onRender : function(li){
34855         var s = document.createElement("span");
34856         s.className = this.itemCls;
34857         s.innerHTML = "&#160;";
34858         this.el = s;
34859         li.addClass("x-menu-sep-li");
34860         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34861     }
34862 });/*
34863  * Based on:
34864  * Ext JS Library 1.1.1
34865  * Copyright(c) 2006-2007, Ext JS, LLC.
34866  *
34867  * Originally Released Under LGPL - original licence link has changed is not relivant.
34868  *
34869  * Fork - LGPL
34870  * <script type="text/javascript">
34871  */
34872 /**
34873  * @class Roo.menu.Item
34874  * @extends Roo.menu.BaseItem
34875  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34876  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34877  * activation and click handling.
34878  * @constructor
34879  * Creates a new Item
34880  * @param {Object} config Configuration options
34881  */
34882 Roo.menu.Item = function(config){
34883     Roo.menu.Item.superclass.constructor.call(this, config);
34884     if(this.menu){
34885         this.menu = Roo.menu.MenuMgr.get(this.menu);
34886     }
34887 };
34888 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34889     
34890     /**
34891      * @cfg {String} text
34892      * The text to show on the menu item.
34893      */
34894     text: '',
34895      /**
34896      * @cfg {String} HTML to render in menu
34897      * The text to show on the menu item (HTML version).
34898      */
34899     html: '',
34900     /**
34901      * @cfg {String} icon
34902      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34903      */
34904     icon: undefined,
34905     /**
34906      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34907      */
34908     itemCls : "x-menu-item",
34909     /**
34910      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34911      */
34912     canActivate : true,
34913     /**
34914      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34915      */
34916     showDelay: 200,
34917     // doc'd in BaseItem
34918     hideDelay: 200,
34919
34920     // private
34921     ctype: "Roo.menu.Item",
34922     
34923     // private
34924     onRender : function(container, position){
34925         var el = document.createElement("a");
34926         el.hideFocus = true;
34927         el.unselectable = "on";
34928         el.href = this.href || "#";
34929         if(this.hrefTarget){
34930             el.target = this.hrefTarget;
34931         }
34932         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34933         
34934         var html = this.html.length ? this.html  : String.format('{0}',this.text);
34935         
34936         el.innerHTML = String.format(
34937                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
34938                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
34939         this.el = el;
34940         Roo.menu.Item.superclass.onRender.call(this, container, position);
34941     },
34942
34943     /**
34944      * Sets the text to display in this menu item
34945      * @param {String} text The text to display
34946      * @param {Boolean} isHTML true to indicate text is pure html.
34947      */
34948     setText : function(text, isHTML){
34949         if (isHTML) {
34950             this.html = text;
34951         } else {
34952             this.text = text;
34953             this.html = '';
34954         }
34955         if(this.rendered){
34956             var html = this.html.length ? this.html  : String.format('{0}',this.text);
34957      
34958             this.el.update(String.format(
34959                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
34960                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34961             this.parentMenu.autoWidth();
34962         }
34963     },
34964
34965     // private
34966     handleClick : function(e){
34967         if(!this.href){ // if no link defined, stop the event automatically
34968             e.stopEvent();
34969         }
34970         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34971     },
34972
34973     // private
34974     activate : function(autoExpand){
34975         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
34976             this.focus();
34977             if(autoExpand){
34978                 this.expandMenu();
34979             }
34980         }
34981         return true;
34982     },
34983
34984     // private
34985     shouldDeactivate : function(e){
34986         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
34987             if(this.menu && this.menu.isVisible()){
34988                 return !this.menu.getEl().getRegion().contains(e.getPoint());
34989             }
34990             return true;
34991         }
34992         return false;
34993     },
34994
34995     // private
34996     deactivate : function(){
34997         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
34998         this.hideMenu();
34999     },
35000
35001     // private
35002     expandMenu : function(autoActivate){
35003         if(!this.disabled && this.menu){
35004             clearTimeout(this.hideTimer);
35005             delete this.hideTimer;
35006             if(!this.menu.isVisible() && !this.showTimer){
35007                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35008             }else if (this.menu.isVisible() && autoActivate){
35009                 this.menu.tryActivate(0, 1);
35010             }
35011         }
35012     },
35013
35014     // private
35015     deferExpand : function(autoActivate){
35016         delete this.showTimer;
35017         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35018         if(autoActivate){
35019             this.menu.tryActivate(0, 1);
35020         }
35021     },
35022
35023     // private
35024     hideMenu : function(){
35025         clearTimeout(this.showTimer);
35026         delete this.showTimer;
35027         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35028             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35029         }
35030     },
35031
35032     // private
35033     deferHide : function(){
35034         delete this.hideTimer;
35035         this.menu.hide();
35036     }
35037 });/*
35038  * Based on:
35039  * Ext JS Library 1.1.1
35040  * Copyright(c) 2006-2007, Ext JS, LLC.
35041  *
35042  * Originally Released Under LGPL - original licence link has changed is not relivant.
35043  *
35044  * Fork - LGPL
35045  * <script type="text/javascript">
35046  */
35047  
35048 /**
35049  * @class Roo.menu.CheckItem
35050  * @extends Roo.menu.Item
35051  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35052  * @constructor
35053  * Creates a new CheckItem
35054  * @param {Object} config Configuration options
35055  */
35056 Roo.menu.CheckItem = function(config){
35057     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35058     this.addEvents({
35059         /**
35060          * @event beforecheckchange
35061          * Fires before the checked value is set, providing an opportunity to cancel if needed
35062          * @param {Roo.menu.CheckItem} this
35063          * @param {Boolean} checked The new checked value that will be set
35064          */
35065         "beforecheckchange" : true,
35066         /**
35067          * @event checkchange
35068          * Fires after the checked value has been set
35069          * @param {Roo.menu.CheckItem} this
35070          * @param {Boolean} checked The checked value that was set
35071          */
35072         "checkchange" : true
35073     });
35074     if(this.checkHandler){
35075         this.on('checkchange', this.checkHandler, this.scope);
35076     }
35077 };
35078 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35079     /**
35080      * @cfg {String} group
35081      * All check items with the same group name will automatically be grouped into a single-select
35082      * radio button group (defaults to '')
35083      */
35084     /**
35085      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35086      */
35087     itemCls : "x-menu-item x-menu-check-item",
35088     /**
35089      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35090      */
35091     groupClass : "x-menu-group-item",
35092
35093     /**
35094      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35095      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35096      * initialized with checked = true will be rendered as checked.
35097      */
35098     checked: false,
35099
35100     // private
35101     ctype: "Roo.menu.CheckItem",
35102
35103     // private
35104     onRender : function(c){
35105         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35106         if(this.group){
35107             this.el.addClass(this.groupClass);
35108         }
35109         Roo.menu.MenuMgr.registerCheckable(this);
35110         if(this.checked){
35111             this.checked = false;
35112             this.setChecked(true, true);
35113         }
35114     },
35115
35116     // private
35117     destroy : function(){
35118         if(this.rendered){
35119             Roo.menu.MenuMgr.unregisterCheckable(this);
35120         }
35121         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35122     },
35123
35124     /**
35125      * Set the checked state of this item
35126      * @param {Boolean} checked The new checked value
35127      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35128      */
35129     setChecked : function(state, suppressEvent){
35130         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35131             if(this.container){
35132                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35133             }
35134             this.checked = state;
35135             if(suppressEvent !== true){
35136                 this.fireEvent("checkchange", this, state);
35137             }
35138         }
35139     },
35140
35141     // private
35142     handleClick : function(e){
35143        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35144            this.setChecked(!this.checked);
35145        }
35146        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35147     }
35148 });/*
35149  * Based on:
35150  * Ext JS Library 1.1.1
35151  * Copyright(c) 2006-2007, Ext JS, LLC.
35152  *
35153  * Originally Released Under LGPL - original licence link has changed is not relivant.
35154  *
35155  * Fork - LGPL
35156  * <script type="text/javascript">
35157  */
35158  
35159 /**
35160  * @class Roo.menu.DateItem
35161  * @extends Roo.menu.Adapter
35162  * A menu item that wraps the {@link Roo.DatPicker} component.
35163  * @constructor
35164  * Creates a new DateItem
35165  * @param {Object} config Configuration options
35166  */
35167 Roo.menu.DateItem = function(config){
35168     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35169     /** The Roo.DatePicker object @type Roo.DatePicker */
35170     this.picker = this.component;
35171     this.addEvents({select: true});
35172     
35173     this.picker.on("render", function(picker){
35174         picker.getEl().swallowEvent("click");
35175         picker.container.addClass("x-menu-date-item");
35176     });
35177
35178     this.picker.on("select", this.onSelect, this);
35179 };
35180
35181 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35182     // private
35183     onSelect : function(picker, date){
35184         this.fireEvent("select", this, date, picker);
35185         Roo.menu.DateItem.superclass.handleClick.call(this);
35186     }
35187 });/*
35188  * Based on:
35189  * Ext JS Library 1.1.1
35190  * Copyright(c) 2006-2007, Ext JS, LLC.
35191  *
35192  * Originally Released Under LGPL - original licence link has changed is not relivant.
35193  *
35194  * Fork - LGPL
35195  * <script type="text/javascript">
35196  */
35197  
35198 /**
35199  * @class Roo.menu.ColorItem
35200  * @extends Roo.menu.Adapter
35201  * A menu item that wraps the {@link Roo.ColorPalette} component.
35202  * @constructor
35203  * Creates a new ColorItem
35204  * @param {Object} config Configuration options
35205  */
35206 Roo.menu.ColorItem = function(config){
35207     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35208     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35209     this.palette = this.component;
35210     this.relayEvents(this.palette, ["select"]);
35211     if(this.selectHandler){
35212         this.on('select', this.selectHandler, this.scope);
35213     }
35214 };
35215 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35216  * Based on:
35217  * Ext JS Library 1.1.1
35218  * Copyright(c) 2006-2007, Ext JS, LLC.
35219  *
35220  * Originally Released Under LGPL - original licence link has changed is not relivant.
35221  *
35222  * Fork - LGPL
35223  * <script type="text/javascript">
35224  */
35225  
35226
35227 /**
35228  * @class Roo.menu.DateMenu
35229  * @extends Roo.menu.Menu
35230  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35231  * @constructor
35232  * Creates a new DateMenu
35233  * @param {Object} config Configuration options
35234  */
35235 Roo.menu.DateMenu = function(config){
35236     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35237     this.plain = true;
35238     var di = new Roo.menu.DateItem(config);
35239     this.add(di);
35240     /**
35241      * The {@link Roo.DatePicker} instance for this DateMenu
35242      * @type DatePicker
35243      */
35244     this.picker = di.picker;
35245     /**
35246      * @event select
35247      * @param {DatePicker} picker
35248      * @param {Date} date
35249      */
35250     this.relayEvents(di, ["select"]);
35251
35252     this.on('beforeshow', function(){
35253         if(this.picker){
35254             this.picker.hideMonthPicker(true);
35255         }
35256     }, this);
35257 };
35258 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35259     cls:'x-date-menu'
35260 });/*
35261  * Based on:
35262  * Ext JS Library 1.1.1
35263  * Copyright(c) 2006-2007, Ext JS, LLC.
35264  *
35265  * Originally Released Under LGPL - original licence link has changed is not relivant.
35266  *
35267  * Fork - LGPL
35268  * <script type="text/javascript">
35269  */
35270  
35271
35272 /**
35273  * @class Roo.menu.ColorMenu
35274  * @extends Roo.menu.Menu
35275  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35276  * @constructor
35277  * Creates a new ColorMenu
35278  * @param {Object} config Configuration options
35279  */
35280 Roo.menu.ColorMenu = function(config){
35281     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35282     this.plain = true;
35283     var ci = new Roo.menu.ColorItem(config);
35284     this.add(ci);
35285     /**
35286      * The {@link Roo.ColorPalette} instance for this ColorMenu
35287      * @type ColorPalette
35288      */
35289     this.palette = ci.palette;
35290     /**
35291      * @event select
35292      * @param {ColorPalette} palette
35293      * @param {String} color
35294      */
35295     this.relayEvents(ci, ["select"]);
35296 };
35297 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35298  * Based on:
35299  * Ext JS Library 1.1.1
35300  * Copyright(c) 2006-2007, Ext JS, LLC.
35301  *
35302  * Originally Released Under LGPL - original licence link has changed is not relivant.
35303  *
35304  * Fork - LGPL
35305  * <script type="text/javascript">
35306  */
35307  
35308 /**
35309  * @class Roo.form.Field
35310  * @extends Roo.BoxComponent
35311  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35312  * @constructor
35313  * Creates a new Field
35314  * @param {Object} config Configuration options
35315  */
35316 Roo.form.Field = function(config){
35317     Roo.form.Field.superclass.constructor.call(this, config);
35318 };
35319
35320 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35321     /**
35322      * @cfg {String} fieldLabel Label to use when rendering a form.
35323      */
35324        /**
35325      * @cfg {String} qtip Mouse over tip
35326      */
35327      
35328     /**
35329      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35330      */
35331     invalidClass : "x-form-invalid",
35332     /**
35333      * @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")
35334      */
35335     invalidText : "The value in this field is invalid",
35336     /**
35337      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35338      */
35339     focusClass : "x-form-focus",
35340     /**
35341      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35342       automatic validation (defaults to "keyup").
35343      */
35344     validationEvent : "keyup",
35345     /**
35346      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35347      */
35348     validateOnBlur : true,
35349     /**
35350      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35351      */
35352     validationDelay : 250,
35353     /**
35354      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35355      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35356      */
35357     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35358     /**
35359      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35360      */
35361     fieldClass : "x-form-field",
35362     /**
35363      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35364      *<pre>
35365 Value         Description
35366 -----------   ----------------------------------------------------------------------
35367 qtip          Display a quick tip when the user hovers over the field
35368 title         Display a default browser title attribute popup
35369 under         Add a block div beneath the field containing the error text
35370 side          Add an error icon to the right of the field with a popup on hover
35371 [element id]  Add the error text directly to the innerHTML of the specified element
35372 </pre>
35373      */
35374     msgTarget : 'qtip',
35375     /**
35376      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35377      */
35378     msgFx : 'normal',
35379
35380     /**
35381      * @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.
35382      */
35383     readOnly : false,
35384
35385     /**
35386      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35387      */
35388     disabled : false,
35389
35390     /**
35391      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35392      */
35393     inputType : undefined,
35394     
35395     /**
35396      * @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).
35397          */
35398         tabIndex : undefined,
35399         
35400     // private
35401     isFormField : true,
35402
35403     // private
35404     hasFocus : false,
35405     /**
35406      * @property {Roo.Element} fieldEl
35407      * Element Containing the rendered Field (with label etc.)
35408      */
35409     /**
35410      * @cfg {Mixed} value A value to initialize this field with.
35411      */
35412     value : undefined,
35413
35414     /**
35415      * @cfg {String} name The field's HTML name attribute.
35416      */
35417     /**
35418      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35419      */
35420
35421         // private ??
35422         initComponent : function(){
35423         Roo.form.Field.superclass.initComponent.call(this);
35424         this.addEvents({
35425             /**
35426              * @event focus
35427              * Fires when this field receives input focus.
35428              * @param {Roo.form.Field} this
35429              */
35430             focus : true,
35431             /**
35432              * @event blur
35433              * Fires when this field loses input focus.
35434              * @param {Roo.form.Field} this
35435              */
35436             blur : true,
35437             /**
35438              * @event specialkey
35439              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35440              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35441              * @param {Roo.form.Field} this
35442              * @param {Roo.EventObject} e The event object
35443              */
35444             specialkey : true,
35445             /**
35446              * @event change
35447              * Fires just before the field blurs if the field value has changed.
35448              * @param {Roo.form.Field} this
35449              * @param {Mixed} newValue The new value
35450              * @param {Mixed} oldValue The original value
35451              */
35452             change : true,
35453             /**
35454              * @event invalid
35455              * Fires after the field has been marked as invalid.
35456              * @param {Roo.form.Field} this
35457              * @param {String} msg The validation message
35458              */
35459             invalid : true,
35460             /**
35461              * @event valid
35462              * Fires after the field has been validated with no errors.
35463              * @param {Roo.form.Field} this
35464              */
35465             valid : true,
35466              /**
35467              * @event keyup
35468              * Fires after the key up
35469              * @param {Roo.form.Field} this
35470              * @param {Roo.EventObject}  e The event Object
35471              */
35472             keyup : true
35473         });
35474     },
35475
35476     /**
35477      * Returns the name attribute of the field if available
35478      * @return {String} name The field name
35479      */
35480     getName: function(){
35481          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35482     },
35483
35484     // private
35485     onRender : function(ct, position){
35486         Roo.form.Field.superclass.onRender.call(this, ct, position);
35487         if(!this.el){
35488             var cfg = this.getAutoCreate();
35489             if(!cfg.name){
35490                 cfg.name = this.name || this.id;
35491             }
35492             if(this.inputType){
35493                 cfg.type = this.inputType;
35494             }
35495             this.el = ct.createChild(cfg, position);
35496         }
35497         var type = this.el.dom.type;
35498         if(type){
35499             if(type == 'password'){
35500                 type = 'text';
35501             }
35502             this.el.addClass('x-form-'+type);
35503         }
35504         if(this.readOnly){
35505             this.el.dom.readOnly = true;
35506         }
35507         if(this.tabIndex !== undefined){
35508             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35509         }
35510
35511         this.el.addClass([this.fieldClass, this.cls]);
35512         this.initValue();
35513     },
35514
35515     /**
35516      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35517      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35518      * @return {Roo.form.Field} this
35519      */
35520     applyTo : function(target){
35521         this.allowDomMove = false;
35522         this.el = Roo.get(target);
35523         this.render(this.el.dom.parentNode);
35524         return this;
35525     },
35526
35527     // private
35528     initValue : function(){
35529         if(this.value !== undefined){
35530             this.setValue(this.value);
35531         }else if(this.el.dom.value.length > 0){
35532             this.setValue(this.el.dom.value);
35533         }
35534     },
35535
35536     /**
35537      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35538      */
35539     isDirty : function() {
35540         if(this.disabled) {
35541             return false;
35542         }
35543         return String(this.getValue()) !== String(this.originalValue);
35544     },
35545
35546     // private
35547     afterRender : function(){
35548         Roo.form.Field.superclass.afterRender.call(this);
35549         this.initEvents();
35550     },
35551
35552     // private
35553     fireKey : function(e){
35554         //Roo.log('field ' + e.getKey());
35555         if(e.isNavKeyPress()){
35556             this.fireEvent("specialkey", this, e);
35557         }
35558     },
35559
35560     /**
35561      * Resets the current field value to the originally loaded value and clears any validation messages
35562      */
35563     reset : function(){
35564         this.setValue(this.originalValue);
35565         this.clearInvalid();
35566     },
35567
35568     // private
35569     initEvents : function(){
35570         // safari killled keypress - so keydown is now used..
35571         this.el.on("keydown" , this.fireKey,  this);
35572         this.el.on("focus", this.onFocus,  this);
35573         this.el.on("blur", this.onBlur,  this);
35574         this.el.relayEvent('keyup', this);
35575
35576         // reference to original value for reset
35577         this.originalValue = this.getValue();
35578     },
35579
35580     // private
35581     onFocus : function(){
35582         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35583             this.el.addClass(this.focusClass);
35584         }
35585         if(!this.hasFocus){
35586             this.hasFocus = true;
35587             this.startValue = this.getValue();
35588             this.fireEvent("focus", this);
35589         }
35590     },
35591
35592     beforeBlur : Roo.emptyFn,
35593
35594     // private
35595     onBlur : function(){
35596         this.beforeBlur();
35597         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35598             this.el.removeClass(this.focusClass);
35599         }
35600         this.hasFocus = false;
35601         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35602             this.validate();
35603         }
35604         var v = this.getValue();
35605         if(String(v) !== String(this.startValue)){
35606             this.fireEvent('change', this, v, this.startValue);
35607         }
35608         this.fireEvent("blur", this);
35609     },
35610
35611     /**
35612      * Returns whether or not the field value is currently valid
35613      * @param {Boolean} preventMark True to disable marking the field invalid
35614      * @return {Boolean} True if the value is valid, else false
35615      */
35616     isValid : function(preventMark){
35617         if(this.disabled){
35618             return true;
35619         }
35620         var restore = this.preventMark;
35621         this.preventMark = preventMark === true;
35622         var v = this.validateValue(this.processValue(this.getRawValue()));
35623         this.preventMark = restore;
35624         return v;
35625     },
35626
35627     /**
35628      * Validates the field value
35629      * @return {Boolean} True if the value is valid, else false
35630      */
35631     validate : function(){
35632         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35633             this.clearInvalid();
35634             return true;
35635         }
35636         return false;
35637     },
35638
35639     processValue : function(value){
35640         return value;
35641     },
35642
35643     // private
35644     // Subclasses should provide the validation implementation by overriding this
35645     validateValue : function(value){
35646         return true;
35647     },
35648
35649     /**
35650      * Mark this field as invalid
35651      * @param {String} msg The validation message
35652      */
35653     markInvalid : function(msg){
35654         if(!this.rendered || this.preventMark){ // not rendered
35655             return;
35656         }
35657         this.el.addClass(this.invalidClass);
35658         msg = msg || this.invalidText;
35659         switch(this.msgTarget){
35660             case 'qtip':
35661                 this.el.dom.qtip = msg;
35662                 this.el.dom.qclass = 'x-form-invalid-tip';
35663                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35664                     Roo.QuickTips.enable();
35665                 }
35666                 break;
35667             case 'title':
35668                 this.el.dom.title = msg;
35669                 break;
35670             case 'under':
35671                 if(!this.errorEl){
35672                     var elp = this.el.findParent('.x-form-element', 5, true);
35673                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35674                     this.errorEl.setWidth(elp.getWidth(true)-20);
35675                 }
35676                 this.errorEl.update(msg);
35677                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35678                 break;
35679             case 'side':
35680                 if(!this.errorIcon){
35681                     var elp = this.el.findParent('.x-form-element', 5, true);
35682                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35683                 }
35684                 this.alignErrorIcon();
35685                 this.errorIcon.dom.qtip = msg;
35686                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35687                 this.errorIcon.show();
35688                 this.on('resize', this.alignErrorIcon, this);
35689                 break;
35690             default:
35691                 var t = Roo.getDom(this.msgTarget);
35692                 t.innerHTML = msg;
35693                 t.style.display = this.msgDisplay;
35694                 break;
35695         }
35696         this.fireEvent('invalid', this, msg);
35697     },
35698
35699     // private
35700     alignErrorIcon : function(){
35701         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35702     },
35703
35704     /**
35705      * Clear any invalid styles/messages for this field
35706      */
35707     clearInvalid : function(){
35708         if(!this.rendered || this.preventMark){ // not rendered
35709             return;
35710         }
35711         this.el.removeClass(this.invalidClass);
35712         switch(this.msgTarget){
35713             case 'qtip':
35714                 this.el.dom.qtip = '';
35715                 break;
35716             case 'title':
35717                 this.el.dom.title = '';
35718                 break;
35719             case 'under':
35720                 if(this.errorEl){
35721                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35722                 }
35723                 break;
35724             case 'side':
35725                 if(this.errorIcon){
35726                     this.errorIcon.dom.qtip = '';
35727                     this.errorIcon.hide();
35728                     this.un('resize', this.alignErrorIcon, this);
35729                 }
35730                 break;
35731             default:
35732                 var t = Roo.getDom(this.msgTarget);
35733                 t.innerHTML = '';
35734                 t.style.display = 'none';
35735                 break;
35736         }
35737         this.fireEvent('valid', this);
35738     },
35739
35740     /**
35741      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35742      * @return {Mixed} value The field value
35743      */
35744     getRawValue : function(){
35745         var v = this.el.getValue();
35746         if(v === this.emptyText){
35747             v = '';
35748         }
35749         return v;
35750     },
35751
35752     /**
35753      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35754      * @return {Mixed} value The field value
35755      */
35756     getValue : function(){
35757         var v = this.el.getValue();
35758         if(v === this.emptyText || v === undefined){
35759             v = '';
35760         }
35761         return v;
35762     },
35763
35764     /**
35765      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35766      * @param {Mixed} value The value to set
35767      */
35768     setRawValue : function(v){
35769         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35770     },
35771
35772     /**
35773      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35774      * @param {Mixed} value The value to set
35775      */
35776     setValue : function(v){
35777         this.value = v;
35778         if(this.rendered){
35779             this.el.dom.value = (v === null || v === undefined ? '' : v);
35780             this.validate();
35781         }
35782     },
35783
35784     adjustSize : function(w, h){
35785         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35786         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35787         return s;
35788     },
35789
35790     adjustWidth : function(tag, w){
35791         tag = tag.toLowerCase();
35792         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35793             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35794                 if(tag == 'input'){
35795                     return w + 2;
35796                 }
35797                 if(tag = 'textarea'){
35798                     return w-2;
35799                 }
35800             }else if(Roo.isOpera){
35801                 if(tag == 'input'){
35802                     return w + 2;
35803                 }
35804                 if(tag = 'textarea'){
35805                     return w-2;
35806                 }
35807             }
35808         }
35809         return w;
35810     }
35811 });
35812
35813
35814 // anything other than normal should be considered experimental
35815 Roo.form.Field.msgFx = {
35816     normal : {
35817         show: function(msgEl, f){
35818             msgEl.setDisplayed('block');
35819         },
35820
35821         hide : function(msgEl, f){
35822             msgEl.setDisplayed(false).update('');
35823         }
35824     },
35825
35826     slide : {
35827         show: function(msgEl, f){
35828             msgEl.slideIn('t', {stopFx:true});
35829         },
35830
35831         hide : function(msgEl, f){
35832             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35833         }
35834     },
35835
35836     slideRight : {
35837         show: function(msgEl, f){
35838             msgEl.fixDisplay();
35839             msgEl.alignTo(f.el, 'tl-tr');
35840             msgEl.slideIn('l', {stopFx:true});
35841         },
35842
35843         hide : function(msgEl, f){
35844             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35845         }
35846     }
35847 };/*
35848  * Based on:
35849  * Ext JS Library 1.1.1
35850  * Copyright(c) 2006-2007, Ext JS, LLC.
35851  *
35852  * Originally Released Under LGPL - original licence link has changed is not relivant.
35853  *
35854  * Fork - LGPL
35855  * <script type="text/javascript">
35856  */
35857  
35858
35859 /**
35860  * @class Roo.form.TextField
35861  * @extends Roo.form.Field
35862  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35863  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35864  * @constructor
35865  * Creates a new TextField
35866  * @param {Object} config Configuration options
35867  */
35868 Roo.form.TextField = function(config){
35869     Roo.form.TextField.superclass.constructor.call(this, config);
35870     this.addEvents({
35871         /**
35872          * @event autosize
35873          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35874          * according to the default logic, but this event provides a hook for the developer to apply additional
35875          * logic at runtime to resize the field if needed.
35876              * @param {Roo.form.Field} this This text field
35877              * @param {Number} width The new field width
35878              */
35879         autosize : true
35880     });
35881 };
35882
35883 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35884     /**
35885      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35886      */
35887     grow : false,
35888     /**
35889      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35890      */
35891     growMin : 30,
35892     /**
35893      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35894      */
35895     growMax : 800,
35896     /**
35897      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35898      */
35899     vtype : null,
35900     /**
35901      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35902      */
35903     maskRe : null,
35904     /**
35905      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35906      */
35907     disableKeyFilter : false,
35908     /**
35909      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35910      */
35911     allowBlank : true,
35912     /**
35913      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35914      */
35915     minLength : 0,
35916     /**
35917      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35918      */
35919     maxLength : Number.MAX_VALUE,
35920     /**
35921      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35922      */
35923     minLengthText : "The minimum length for this field is {0}",
35924     /**
35925      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35926      */
35927     maxLengthText : "The maximum length for this field is {0}",
35928     /**
35929      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35930      */
35931     selectOnFocus : false,
35932     /**
35933      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35934      */
35935     blankText : "This field is required",
35936     /**
35937      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35938      * If available, this function will be called only after the basic validators all return true, and will be passed the
35939      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35940      */
35941     validator : null,
35942     /**
35943      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35944      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35945      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35946      */
35947     regex : null,
35948     /**
35949      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35950      */
35951     regexText : "",
35952     /**
35953      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35954      */
35955     emptyText : null,
35956     /**
35957      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35958      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35959      */
35960     emptyClass : 'x-form-empty-field',
35961
35962     // private
35963     initEvents : function(){
35964         Roo.form.TextField.superclass.initEvents.call(this);
35965         if(this.validationEvent == 'keyup'){
35966             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35967             this.el.on('keyup', this.filterValidation, this);
35968         }
35969         else if(this.validationEvent !== false){
35970             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35971         }
35972         if(this.selectOnFocus || this.emptyText){
35973             this.on("focus", this.preFocus, this);
35974             if(this.emptyText){
35975                 this.on('blur', this.postBlur, this);
35976                 this.applyEmptyText();
35977             }
35978         }
35979         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
35980             this.el.on("keypress", this.filterKeys, this);
35981         }
35982         if(this.grow){
35983             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
35984             this.el.on("click", this.autoSize,  this);
35985         }
35986     },
35987
35988     processValue : function(value){
35989         if(this.stripCharsRe){
35990             var newValue = value.replace(this.stripCharsRe, '');
35991             if(newValue !== value){
35992                 this.setRawValue(newValue);
35993                 return newValue;
35994             }
35995         }
35996         return value;
35997     },
35998
35999     filterValidation : function(e){
36000         if(!e.isNavKeyPress()){
36001             this.validationTask.delay(this.validationDelay);
36002         }
36003     },
36004
36005     // private
36006     onKeyUp : function(e){
36007         if(!e.isNavKeyPress()){
36008             this.autoSize();
36009         }
36010     },
36011
36012     /**
36013      * Resets the current field value to the originally-loaded value and clears any validation messages.
36014      * Also adds emptyText and emptyClass if the original value was blank.
36015      */
36016     reset : function(){
36017         Roo.form.TextField.superclass.reset.call(this);
36018         this.applyEmptyText();
36019     },
36020
36021     applyEmptyText : function(){
36022         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36023             this.setRawValue(this.emptyText);
36024             this.el.addClass(this.emptyClass);
36025         }
36026     },
36027
36028     // private
36029     preFocus : function(){
36030         if(this.emptyText){
36031             if(this.el.dom.value == this.emptyText){
36032                 this.setRawValue('');
36033             }
36034             this.el.removeClass(this.emptyClass);
36035         }
36036         if(this.selectOnFocus){
36037             this.el.dom.select();
36038         }
36039     },
36040
36041     // private
36042     postBlur : function(){
36043         this.applyEmptyText();
36044     },
36045
36046     // private
36047     filterKeys : function(e){
36048         var k = e.getKey();
36049         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36050             return;
36051         }
36052         var c = e.getCharCode(), cc = String.fromCharCode(c);
36053         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36054             return;
36055         }
36056         if(!this.maskRe.test(cc)){
36057             e.stopEvent();
36058         }
36059     },
36060
36061     setValue : function(v){
36062         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36063             this.el.removeClass(this.emptyClass);
36064         }
36065         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36066         this.applyEmptyText();
36067         this.autoSize();
36068     },
36069
36070     /**
36071      * Validates a value according to the field's validation rules and marks the field as invalid
36072      * if the validation fails
36073      * @param {Mixed} value The value to validate
36074      * @return {Boolean} True if the value is valid, else false
36075      */
36076     validateValue : function(value){
36077         if(value.length < 1 || value === this.emptyText){ // if it's blank
36078              if(this.allowBlank){
36079                 this.clearInvalid();
36080                 return true;
36081              }else{
36082                 this.markInvalid(this.blankText);
36083                 return false;
36084              }
36085         }
36086         if(value.length < this.minLength){
36087             this.markInvalid(String.format(this.minLengthText, this.minLength));
36088             return false;
36089         }
36090         if(value.length > this.maxLength){
36091             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36092             return false;
36093         }
36094         if(this.vtype){
36095             var vt = Roo.form.VTypes;
36096             if(!vt[this.vtype](value, this)){
36097                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36098                 return false;
36099             }
36100         }
36101         if(typeof this.validator == "function"){
36102             var msg = this.validator(value);
36103             if(msg !== true){
36104                 this.markInvalid(msg);
36105                 return false;
36106             }
36107         }
36108         if(this.regex && !this.regex.test(value)){
36109             this.markInvalid(this.regexText);
36110             return false;
36111         }
36112         return true;
36113     },
36114
36115     /**
36116      * Selects text in this field
36117      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36118      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36119      */
36120     selectText : function(start, end){
36121         var v = this.getRawValue();
36122         if(v.length > 0){
36123             start = start === undefined ? 0 : start;
36124             end = end === undefined ? v.length : end;
36125             var d = this.el.dom;
36126             if(d.setSelectionRange){
36127                 d.setSelectionRange(start, end);
36128             }else if(d.createTextRange){
36129                 var range = d.createTextRange();
36130                 range.moveStart("character", start);
36131                 range.moveEnd("character", v.length-end);
36132                 range.select();
36133             }
36134         }
36135     },
36136
36137     /**
36138      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36139      * This only takes effect if grow = true, and fires the autosize event.
36140      */
36141     autoSize : function(){
36142         if(!this.grow || !this.rendered){
36143             return;
36144         }
36145         if(!this.metrics){
36146             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36147         }
36148         var el = this.el;
36149         var v = el.dom.value;
36150         var d = document.createElement('div');
36151         d.appendChild(document.createTextNode(v));
36152         v = d.innerHTML;
36153         d = null;
36154         v += "&#160;";
36155         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36156         this.el.setWidth(w);
36157         this.fireEvent("autosize", this, w);
36158     }
36159 });/*
36160  * Based on:
36161  * Ext JS Library 1.1.1
36162  * Copyright(c) 2006-2007, Ext JS, LLC.
36163  *
36164  * Originally Released Under LGPL - original licence link has changed is not relivant.
36165  *
36166  * Fork - LGPL
36167  * <script type="text/javascript">
36168  */
36169  
36170 /**
36171  * @class Roo.form.Hidden
36172  * @extends Roo.form.TextField
36173  * Simple Hidden element used on forms 
36174  * 
36175  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36176  * 
36177  * @constructor
36178  * Creates a new Hidden form element.
36179  * @param {Object} config Configuration options
36180  */
36181
36182
36183
36184 // easy hidden field...
36185 Roo.form.Hidden = function(config){
36186     Roo.form.Hidden.superclass.constructor.call(this, config);
36187 };
36188   
36189 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36190     fieldLabel:      '',
36191     inputType:      'hidden',
36192     width:          50,
36193     allowBlank:     true,
36194     labelSeparator: '',
36195     hidden:         true,
36196     itemCls :       'x-form-item-display-none'
36197
36198
36199 });
36200
36201
36202 /*
36203  * Based on:
36204  * Ext JS Library 1.1.1
36205  * Copyright(c) 2006-2007, Ext JS, LLC.
36206  *
36207  * Originally Released Under LGPL - original licence link has changed is not relivant.
36208  *
36209  * Fork - LGPL
36210  * <script type="text/javascript">
36211  */
36212  
36213 /**
36214  * @class Roo.form.TriggerField
36215  * @extends Roo.form.TextField
36216  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36217  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36218  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36219  * for which you can provide a custom implementation.  For example:
36220  * <pre><code>
36221 var trigger = new Roo.form.TriggerField();
36222 trigger.onTriggerClick = myTriggerFn;
36223 trigger.applyTo('my-field');
36224 </code></pre>
36225  *
36226  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36227  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36228  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36229  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36230  * @constructor
36231  * Create a new TriggerField.
36232  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36233  * to the base TextField)
36234  */
36235 Roo.form.TriggerField = function(config){
36236     this.mimicing = false;
36237     Roo.form.TriggerField.superclass.constructor.call(this, config);
36238 };
36239
36240 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36241     /**
36242      * @cfg {String} triggerClass A CSS class to apply to the trigger
36243      */
36244     /**
36245      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36246      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36247      */
36248     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36249     /**
36250      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36251      */
36252     hideTrigger:false,
36253
36254     /** @cfg {Boolean} grow @hide */
36255     /** @cfg {Number} growMin @hide */
36256     /** @cfg {Number} growMax @hide */
36257
36258     /**
36259      * @hide 
36260      * @method
36261      */
36262     autoSize: Roo.emptyFn,
36263     // private
36264     monitorTab : true,
36265     // private
36266     deferHeight : true,
36267
36268     
36269     actionMode : 'wrap',
36270     // private
36271     onResize : function(w, h){
36272         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36273         if(typeof w == 'number'){
36274             var x = w - this.trigger.getWidth();
36275             this.el.setWidth(this.adjustWidth('input', x));
36276             this.trigger.setStyle('left', x+'px');
36277         }
36278     },
36279
36280     // private
36281     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36282
36283     // private
36284     getResizeEl : function(){
36285         return this.wrap;
36286     },
36287
36288     // private
36289     getPositionEl : function(){
36290         return this.wrap;
36291     },
36292
36293     // private
36294     alignErrorIcon : function(){
36295         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36296     },
36297
36298     // private
36299     onRender : function(ct, position){
36300         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36301         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36302         this.trigger = this.wrap.createChild(this.triggerConfig ||
36303                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36304         if(this.hideTrigger){
36305             this.trigger.setDisplayed(false);
36306         }
36307         this.initTrigger();
36308         if(!this.width){
36309             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36310         }
36311     },
36312
36313     // private
36314     initTrigger : function(){
36315         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36316         this.trigger.addClassOnOver('x-form-trigger-over');
36317         this.trigger.addClassOnClick('x-form-trigger-click');
36318     },
36319
36320     // private
36321     onDestroy : function(){
36322         if(this.trigger){
36323             this.trigger.removeAllListeners();
36324             this.trigger.remove();
36325         }
36326         if(this.wrap){
36327             this.wrap.remove();
36328         }
36329         Roo.form.TriggerField.superclass.onDestroy.call(this);
36330     },
36331
36332     // private
36333     onFocus : function(){
36334         Roo.form.TriggerField.superclass.onFocus.call(this);
36335         if(!this.mimicing){
36336             this.wrap.addClass('x-trigger-wrap-focus');
36337             this.mimicing = true;
36338             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36339             if(this.monitorTab){
36340                 this.el.on("keydown", this.checkTab, this);
36341             }
36342         }
36343     },
36344
36345     // private
36346     checkTab : function(e){
36347         if(e.getKey() == e.TAB){
36348             this.triggerBlur();
36349         }
36350     },
36351
36352     // private
36353     onBlur : function(){
36354         // do nothing
36355     },
36356
36357     // private
36358     mimicBlur : function(e, t){
36359         if(!this.wrap.contains(t) && this.validateBlur()){
36360             this.triggerBlur();
36361         }
36362     },
36363
36364     // private
36365     triggerBlur : function(){
36366         this.mimicing = false;
36367         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36368         if(this.monitorTab){
36369             this.el.un("keydown", this.checkTab, this);
36370         }
36371         this.wrap.removeClass('x-trigger-wrap-focus');
36372         Roo.form.TriggerField.superclass.onBlur.call(this);
36373     },
36374
36375     // private
36376     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36377     validateBlur : function(e, t){
36378         return true;
36379     },
36380
36381     // private
36382     onDisable : function(){
36383         Roo.form.TriggerField.superclass.onDisable.call(this);
36384         if(this.wrap){
36385             this.wrap.addClass('x-item-disabled');
36386         }
36387     },
36388
36389     // private
36390     onEnable : function(){
36391         Roo.form.TriggerField.superclass.onEnable.call(this);
36392         if(this.wrap){
36393             this.wrap.removeClass('x-item-disabled');
36394         }
36395     },
36396
36397     // private
36398     onShow : function(){
36399         var ae = this.getActionEl();
36400         
36401         if(ae){
36402             ae.dom.style.display = '';
36403             ae.dom.style.visibility = 'visible';
36404         }
36405     },
36406
36407     // private
36408     
36409     onHide : function(){
36410         var ae = this.getActionEl();
36411         ae.dom.style.display = 'none';
36412     },
36413
36414     /**
36415      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36416      * by an implementing function.
36417      * @method
36418      * @param {EventObject} e
36419      */
36420     onTriggerClick : Roo.emptyFn
36421 });
36422
36423 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36424 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36425 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36426 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36427     initComponent : function(){
36428         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36429
36430         this.triggerConfig = {
36431             tag:'span', cls:'x-form-twin-triggers', cn:[
36432             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36433             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36434         ]};
36435     },
36436
36437     getTrigger : function(index){
36438         return this.triggers[index];
36439     },
36440
36441     initTrigger : function(){
36442         var ts = this.trigger.select('.x-form-trigger', true);
36443         this.wrap.setStyle('overflow', 'hidden');
36444         var triggerField = this;
36445         ts.each(function(t, all, index){
36446             t.hide = function(){
36447                 var w = triggerField.wrap.getWidth();
36448                 this.dom.style.display = 'none';
36449                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36450             };
36451             t.show = function(){
36452                 var w = triggerField.wrap.getWidth();
36453                 this.dom.style.display = '';
36454                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36455             };
36456             var triggerIndex = 'Trigger'+(index+1);
36457
36458             if(this['hide'+triggerIndex]){
36459                 t.dom.style.display = 'none';
36460             }
36461             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36462             t.addClassOnOver('x-form-trigger-over');
36463             t.addClassOnClick('x-form-trigger-click');
36464         }, this);
36465         this.triggers = ts.elements;
36466     },
36467
36468     onTrigger1Click : Roo.emptyFn,
36469     onTrigger2Click : Roo.emptyFn
36470 });/*
36471  * Based on:
36472  * Ext JS Library 1.1.1
36473  * Copyright(c) 2006-2007, Ext JS, LLC.
36474  *
36475  * Originally Released Under LGPL - original licence link has changed is not relivant.
36476  *
36477  * Fork - LGPL
36478  * <script type="text/javascript">
36479  */
36480  
36481 /**
36482  * @class Roo.form.TextArea
36483  * @extends Roo.form.TextField
36484  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36485  * support for auto-sizing.
36486  * @constructor
36487  * Creates a new TextArea
36488  * @param {Object} config Configuration options
36489  */
36490 Roo.form.TextArea = function(config){
36491     Roo.form.TextArea.superclass.constructor.call(this, config);
36492     // these are provided exchanges for backwards compat
36493     // minHeight/maxHeight were replaced by growMin/growMax to be
36494     // compatible with TextField growing config values
36495     if(this.minHeight !== undefined){
36496         this.growMin = this.minHeight;
36497     }
36498     if(this.maxHeight !== undefined){
36499         this.growMax = this.maxHeight;
36500     }
36501 };
36502
36503 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36504     /**
36505      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36506      */
36507     growMin : 60,
36508     /**
36509      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36510      */
36511     growMax: 1000,
36512     /**
36513      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36514      * in the field (equivalent to setting overflow: hidden, defaults to false)
36515      */
36516     preventScrollbars: false,
36517     /**
36518      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36519      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36520      */
36521
36522     // private
36523     onRender : function(ct, position){
36524         if(!this.el){
36525             this.defaultAutoCreate = {
36526                 tag: "textarea",
36527                 style:"width:300px;height:60px;",
36528                 autocomplete: "off"
36529             };
36530         }
36531         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36532         if(this.grow){
36533             this.textSizeEl = Roo.DomHelper.append(document.body, {
36534                 tag: "pre", cls: "x-form-grow-sizer"
36535             });
36536             if(this.preventScrollbars){
36537                 this.el.setStyle("overflow", "hidden");
36538             }
36539             this.el.setHeight(this.growMin);
36540         }
36541     },
36542
36543     onDestroy : function(){
36544         if(this.textSizeEl){
36545             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36546         }
36547         Roo.form.TextArea.superclass.onDestroy.call(this);
36548     },
36549
36550     // private
36551     onKeyUp : function(e){
36552         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36553             this.autoSize();
36554         }
36555     },
36556
36557     /**
36558      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36559      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36560      */
36561     autoSize : function(){
36562         if(!this.grow || !this.textSizeEl){
36563             return;
36564         }
36565         var el = this.el;
36566         var v = el.dom.value;
36567         var ts = this.textSizeEl;
36568
36569         ts.innerHTML = '';
36570         ts.appendChild(document.createTextNode(v));
36571         v = ts.innerHTML;
36572
36573         Roo.fly(ts).setWidth(this.el.getWidth());
36574         if(v.length < 1){
36575             v = "&#160;&#160;";
36576         }else{
36577             if(Roo.isIE){
36578                 v = v.replace(/\n/g, '<p>&#160;</p>');
36579             }
36580             v += "&#160;\n&#160;";
36581         }
36582         ts.innerHTML = v;
36583         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36584         if(h != this.lastHeight){
36585             this.lastHeight = h;
36586             this.el.setHeight(h);
36587             this.fireEvent("autosize", this, h);
36588         }
36589     }
36590 });/*
36591  * Based on:
36592  * Ext JS Library 1.1.1
36593  * Copyright(c) 2006-2007, Ext JS, LLC.
36594  *
36595  * Originally Released Under LGPL - original licence link has changed is not relivant.
36596  *
36597  * Fork - LGPL
36598  * <script type="text/javascript">
36599  */
36600  
36601
36602 /**
36603  * @class Roo.form.NumberField
36604  * @extends Roo.form.TextField
36605  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36606  * @constructor
36607  * Creates a new NumberField
36608  * @param {Object} config Configuration options
36609  */
36610 Roo.form.NumberField = function(config){
36611     Roo.form.NumberField.superclass.constructor.call(this, config);
36612 };
36613
36614 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36615     /**
36616      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36617      */
36618     fieldClass: "x-form-field x-form-num-field",
36619     /**
36620      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36621      */
36622     allowDecimals : true,
36623     /**
36624      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36625      */
36626     decimalSeparator : ".",
36627     /**
36628      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36629      */
36630     decimalPrecision : 2,
36631     /**
36632      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36633      */
36634     allowNegative : true,
36635     /**
36636      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36637      */
36638     minValue : Number.NEGATIVE_INFINITY,
36639     /**
36640      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36641      */
36642     maxValue : Number.MAX_VALUE,
36643     /**
36644      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36645      */
36646     minText : "The minimum value for this field is {0}",
36647     /**
36648      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36649      */
36650     maxText : "The maximum value for this field is {0}",
36651     /**
36652      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36653      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36654      */
36655     nanText : "{0} is not a valid number",
36656
36657     // private
36658     initEvents : function(){
36659         Roo.form.NumberField.superclass.initEvents.call(this);
36660         var allowed = "0123456789";
36661         if(this.allowDecimals){
36662             allowed += this.decimalSeparator;
36663         }
36664         if(this.allowNegative){
36665             allowed += "-";
36666         }
36667         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36668         var keyPress = function(e){
36669             var k = e.getKey();
36670             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36671                 return;
36672             }
36673             var c = e.getCharCode();
36674             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36675                 e.stopEvent();
36676             }
36677         };
36678         this.el.on("keypress", keyPress, this);
36679     },
36680
36681     // private
36682     validateValue : function(value){
36683         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36684             return false;
36685         }
36686         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36687              return true;
36688         }
36689         var num = this.parseValue(value);
36690         if(isNaN(num)){
36691             this.markInvalid(String.format(this.nanText, value));
36692             return false;
36693         }
36694         if(num < this.minValue){
36695             this.markInvalid(String.format(this.minText, this.minValue));
36696             return false;
36697         }
36698         if(num > this.maxValue){
36699             this.markInvalid(String.format(this.maxText, this.maxValue));
36700             return false;
36701         }
36702         return true;
36703     },
36704
36705     getValue : function(){
36706         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36707     },
36708
36709     // private
36710     parseValue : function(value){
36711         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36712         return isNaN(value) ? '' : value;
36713     },
36714
36715     // private
36716     fixPrecision : function(value){
36717         var nan = isNaN(value);
36718         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36719             return nan ? '' : value;
36720         }
36721         return parseFloat(value).toFixed(this.decimalPrecision);
36722     },
36723
36724     setValue : function(v){
36725         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36726     },
36727
36728     // private
36729     decimalPrecisionFcn : function(v){
36730         return Math.floor(v);
36731     },
36732
36733     beforeBlur : function(){
36734         var v = this.parseValue(this.getRawValue());
36735         if(v){
36736             this.setValue(this.fixPrecision(v));
36737         }
36738     }
36739 });/*
36740  * Based on:
36741  * Ext JS Library 1.1.1
36742  * Copyright(c) 2006-2007, Ext JS, LLC.
36743  *
36744  * Originally Released Under LGPL - original licence link has changed is not relivant.
36745  *
36746  * Fork - LGPL
36747  * <script type="text/javascript">
36748  */
36749  
36750 /**
36751  * @class Roo.form.DateField
36752  * @extends Roo.form.TriggerField
36753  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36754 * @constructor
36755 * Create a new DateField
36756 * @param {Object} config
36757  */
36758 Roo.form.DateField = function(config){
36759     Roo.form.DateField.superclass.constructor.call(this, config);
36760     
36761       this.addEvents({
36762          
36763         /**
36764          * @event select
36765          * Fires when a date is selected
36766              * @param {Roo.form.DateField} combo This combo box
36767              * @param {Date} date The date selected
36768              */
36769         'select' : true
36770          
36771     });
36772     
36773     
36774     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36775     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36776     this.ddMatch = null;
36777     if(this.disabledDates){
36778         var dd = this.disabledDates;
36779         var re = "(?:";
36780         for(var i = 0; i < dd.length; i++){
36781             re += dd[i];
36782             if(i != dd.length-1) re += "|";
36783         }
36784         this.ddMatch = new RegExp(re + ")");
36785     }
36786 };
36787
36788 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36789     /**
36790      * @cfg {String} format
36791      * The default date format string which can be overriden for localization support.  The format must be
36792      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36793      */
36794     format : "m/d/y",
36795     /**
36796      * @cfg {String} altFormats
36797      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36798      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36799      */
36800     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36801     /**
36802      * @cfg {Array} disabledDays
36803      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36804      */
36805     disabledDays : null,
36806     /**
36807      * @cfg {String} disabledDaysText
36808      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36809      */
36810     disabledDaysText : "Disabled",
36811     /**
36812      * @cfg {Array} disabledDates
36813      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36814      * expression so they are very powerful. Some examples:
36815      * <ul>
36816      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36817      * <li>["03/08", "09/16"] would disable those days for every year</li>
36818      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36819      * <li>["03/../2006"] would disable every day in March 2006</li>
36820      * <li>["^03"] would disable every day in every March</li>
36821      * </ul>
36822      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36823      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36824      */
36825     disabledDates : null,
36826     /**
36827      * @cfg {String} disabledDatesText
36828      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36829      */
36830     disabledDatesText : "Disabled",
36831     /**
36832      * @cfg {Date/String} minValue
36833      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36834      * valid format (defaults to null).
36835      */
36836     minValue : null,
36837     /**
36838      * @cfg {Date/String} maxValue
36839      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36840      * valid format (defaults to null).
36841      */
36842     maxValue : null,
36843     /**
36844      * @cfg {String} minText
36845      * The error text to display when the date in the cell is before minValue (defaults to
36846      * 'The date in this field must be after {minValue}').
36847      */
36848     minText : "The date in this field must be equal to or after {0}",
36849     /**
36850      * @cfg {String} maxText
36851      * The error text to display when the date in the cell is after maxValue (defaults to
36852      * 'The date in this field must be before {maxValue}').
36853      */
36854     maxText : "The date in this field must be equal to or before {0}",
36855     /**
36856      * @cfg {String} invalidText
36857      * The error text to display when the date in the field is invalid (defaults to
36858      * '{value} is not a valid date - it must be in the format {format}').
36859      */
36860     invalidText : "{0} is not a valid date - it must be in the format {1}",
36861     /**
36862      * @cfg {String} triggerClass
36863      * An additional CSS class used to style the trigger button.  The trigger will always get the
36864      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36865      * which displays a calendar icon).
36866      */
36867     triggerClass : 'x-form-date-trigger',
36868     
36869
36870     /**
36871      * @cfg {bool} useIso
36872      * if enabled, then the date field will use a hidden field to store the 
36873      * real value as iso formated date. default (false)
36874      */ 
36875     useIso : false,
36876     /**
36877      * @cfg {String/Object} autoCreate
36878      * A DomHelper element spec, or true for a default element spec (defaults to
36879      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36880      */ 
36881     // private
36882     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36883     
36884     // private
36885     hiddenField: false,
36886     
36887     onRender : function(ct, position)
36888     {
36889         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36890         if (this.useIso) {
36891             this.el.dom.removeAttribute('name'); 
36892             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36893                     'before', true);
36894             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36895             // prevent input submission
36896             this.hiddenName = this.name;
36897         }
36898             
36899             
36900     },
36901     
36902     // private
36903     validateValue : function(value)
36904     {
36905         value = this.formatDate(value);
36906         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36907             return false;
36908         }
36909         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36910              return true;
36911         }
36912         var svalue = value;
36913         value = this.parseDate(value);
36914         if(!value){
36915             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36916             return false;
36917         }
36918         var time = value.getTime();
36919         if(this.minValue && time < this.minValue.getTime()){
36920             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36921             return false;
36922         }
36923         if(this.maxValue && time > this.maxValue.getTime()){
36924             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36925             return false;
36926         }
36927         if(this.disabledDays){
36928             var day = value.getDay();
36929             for(var i = 0; i < this.disabledDays.length; i++) {
36930                 if(day === this.disabledDays[i]){
36931                     this.markInvalid(this.disabledDaysText);
36932                     return false;
36933                 }
36934             }
36935         }
36936         var fvalue = this.formatDate(value);
36937         if(this.ddMatch && this.ddMatch.test(fvalue)){
36938             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36939             return false;
36940         }
36941         return true;
36942     },
36943
36944     // private
36945     // Provides logic to override the default TriggerField.validateBlur which just returns true
36946     validateBlur : function(){
36947         return !this.menu || !this.menu.isVisible();
36948     },
36949
36950     /**
36951      * Returns the current date value of the date field.
36952      * @return {Date} The date value
36953      */
36954     getValue : function(){
36955         
36956         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36957     },
36958
36959     /**
36960      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36961      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36962      * (the default format used is "m/d/y").
36963      * <br />Usage:
36964      * <pre><code>
36965 //All of these calls set the same date value (May 4, 2006)
36966
36967 //Pass a date object:
36968 var dt = new Date('5/4/06');
36969 dateField.setValue(dt);
36970
36971 //Pass a date string (default format):
36972 dateField.setValue('5/4/06');
36973
36974 //Pass a date string (custom format):
36975 dateField.format = 'Y-m-d';
36976 dateField.setValue('2006-5-4');
36977 </code></pre>
36978      * @param {String/Date} date The date or valid date string
36979      */
36980     setValue : function(date){
36981         if (this.hiddenField) {
36982             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
36983         }
36984         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
36985     },
36986
36987     // private
36988     parseDate : function(value){
36989         if(!value || value instanceof Date){
36990             return value;
36991         }
36992         var v = Date.parseDate(value, this.format);
36993         if(!v && this.altFormats){
36994             if(!this.altFormatsArray){
36995                 this.altFormatsArray = this.altFormats.split("|");
36996             }
36997             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
36998                 v = Date.parseDate(value, this.altFormatsArray[i]);
36999             }
37000         }
37001         return v;
37002     },
37003
37004     // private
37005     formatDate : function(date, fmt){
37006         return (!date || !(date instanceof Date)) ?
37007                date : date.dateFormat(fmt || this.format);
37008     },
37009
37010     // private
37011     menuListeners : {
37012         select: function(m, d){
37013             this.setValue(d);
37014             this.fireEvent('select', this, d);
37015         },
37016         show : function(){ // retain focus styling
37017             this.onFocus();
37018         },
37019         hide : function(){
37020             this.focus.defer(10, this);
37021             var ml = this.menuListeners;
37022             this.menu.un("select", ml.select,  this);
37023             this.menu.un("show", ml.show,  this);
37024             this.menu.un("hide", ml.hide,  this);
37025         }
37026     },
37027
37028     // private
37029     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37030     onTriggerClick : function(){
37031         if(this.disabled){
37032             return;
37033         }
37034         if(this.menu == null){
37035             this.menu = new Roo.menu.DateMenu();
37036         }
37037         Roo.apply(this.menu.picker,  {
37038             showClear: this.allowBlank,
37039             minDate : this.minValue,
37040             maxDate : this.maxValue,
37041             disabledDatesRE : this.ddMatch,
37042             disabledDatesText : this.disabledDatesText,
37043             disabledDays : this.disabledDays,
37044             disabledDaysText : this.disabledDaysText,
37045             format : this.format,
37046             minText : String.format(this.minText, this.formatDate(this.minValue)),
37047             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37048         });
37049         this.menu.on(Roo.apply({}, this.menuListeners, {
37050             scope:this
37051         }));
37052         this.menu.picker.setValue(this.getValue() || new Date());
37053         this.menu.show(this.el, "tl-bl?");
37054     },
37055
37056     beforeBlur : function(){
37057         var v = this.parseDate(this.getRawValue());
37058         if(v){
37059             this.setValue(v);
37060         }
37061     }
37062
37063     /** @cfg {Boolean} grow @hide */
37064     /** @cfg {Number} growMin @hide */
37065     /** @cfg {Number} growMax @hide */
37066     /**
37067      * @hide
37068      * @method autoSize
37069      */
37070 });/*
37071  * Based on:
37072  * Ext JS Library 1.1.1
37073  * Copyright(c) 2006-2007, Ext JS, LLC.
37074  *
37075  * Originally Released Under LGPL - original licence link has changed is not relivant.
37076  *
37077  * Fork - LGPL
37078  * <script type="text/javascript">
37079  */
37080  
37081
37082 /**
37083  * @class Roo.form.ComboBox
37084  * @extends Roo.form.TriggerField
37085  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37086  * @constructor
37087  * Create a new ComboBox.
37088  * @param {Object} config Configuration options
37089  */
37090 Roo.form.ComboBox = function(config){
37091     Roo.form.ComboBox.superclass.constructor.call(this, config);
37092     this.addEvents({
37093         /**
37094          * @event expand
37095          * Fires when the dropdown list is expanded
37096              * @param {Roo.form.ComboBox} combo This combo box
37097              */
37098         'expand' : true,
37099         /**
37100          * @event collapse
37101          * Fires when the dropdown list is collapsed
37102              * @param {Roo.form.ComboBox} combo This combo box
37103              */
37104         'collapse' : true,
37105         /**
37106          * @event beforeselect
37107          * Fires before a list item is selected. Return false to cancel the selection.
37108              * @param {Roo.form.ComboBox} combo This combo box
37109              * @param {Roo.data.Record} record The data record returned from the underlying store
37110              * @param {Number} index The index of the selected item in the dropdown list
37111              */
37112         'beforeselect' : true,
37113         /**
37114          * @event select
37115          * Fires when a list item is selected
37116              * @param {Roo.form.ComboBox} combo This combo box
37117              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37118              * @param {Number} index The index of the selected item in the dropdown list
37119              */
37120         'select' : true,
37121         /**
37122          * @event beforequery
37123          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37124          * The event object passed has these properties:
37125              * @param {Roo.form.ComboBox} combo This combo box
37126              * @param {String} query The query
37127              * @param {Boolean} forceAll true to force "all" query
37128              * @param {Boolean} cancel true to cancel the query
37129              * @param {Object} e The query event object
37130              */
37131         'beforequery': true,
37132          /**
37133          * @event add
37134          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37135              * @param {Roo.form.ComboBox} combo This combo box
37136              */
37137         'add' : true,
37138         /**
37139          * @event edit
37140          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37141              * @param {Roo.form.ComboBox} combo This combo box
37142              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37143              */
37144         'edit' : true
37145         
37146         
37147     });
37148     if(this.transform){
37149         this.allowDomMove = false;
37150         var s = Roo.getDom(this.transform);
37151         if(!this.hiddenName){
37152             this.hiddenName = s.name;
37153         }
37154         if(!this.store){
37155             this.mode = 'local';
37156             var d = [], opts = s.options;
37157             for(var i = 0, len = opts.length;i < len; i++){
37158                 var o = opts[i];
37159                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37160                 if(o.selected) {
37161                     this.value = value;
37162                 }
37163                 d.push([value, o.text]);
37164             }
37165             this.store = new Roo.data.SimpleStore({
37166                 'id': 0,
37167                 fields: ['value', 'text'],
37168                 data : d
37169             });
37170             this.valueField = 'value';
37171             this.displayField = 'text';
37172         }
37173         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37174         if(!this.lazyRender){
37175             this.target = true;
37176             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37177             s.parentNode.removeChild(s); // remove it
37178             this.render(this.el.parentNode);
37179         }else{
37180             s.parentNode.removeChild(s); // remove it
37181         }
37182
37183     }
37184     if (this.store) {
37185         this.store = Roo.factory(this.store, Roo.data);
37186     }
37187     
37188     this.selectedIndex = -1;
37189     if(this.mode == 'local'){
37190         if(config.queryDelay === undefined){
37191             this.queryDelay = 10;
37192         }
37193         if(config.minChars === undefined){
37194             this.minChars = 0;
37195         }
37196     }
37197 };
37198
37199 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37200     /**
37201      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37202      */
37203     /**
37204      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37205      * rendering into an Roo.Editor, defaults to false)
37206      */
37207     /**
37208      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37209      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37210      */
37211     /**
37212      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37213      */
37214     /**
37215      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37216      * the dropdown list (defaults to undefined, with no header element)
37217      */
37218
37219      /**
37220      * @cfg {String/Roo.Template} tpl The template to use to render the output
37221      */
37222      
37223     // private
37224     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37225     /**
37226      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37227      */
37228     listWidth: undefined,
37229     /**
37230      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37231      * mode = 'remote' or 'text' if mode = 'local')
37232      */
37233     displayField: undefined,
37234     /**
37235      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37236      * mode = 'remote' or 'value' if mode = 'local'). 
37237      * Note: use of a valueField requires the user make a selection
37238      * in order for a value to be mapped.
37239      */
37240     valueField: undefined,
37241     /**
37242      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37243      * field's data value (defaults to the underlying DOM element's name)
37244      */
37245     hiddenName: undefined,
37246     /**
37247      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37248      */
37249     listClass: '',
37250     /**
37251      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37252      */
37253     selectedClass: 'x-combo-selected',
37254     /**
37255      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37256      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37257      * which displays a downward arrow icon).
37258      */
37259     triggerClass : 'x-form-arrow-trigger',
37260     /**
37261      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37262      */
37263     shadow:'sides',
37264     /**
37265      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37266      * anchor positions (defaults to 'tl-bl')
37267      */
37268     listAlign: 'tl-bl?',
37269     /**
37270      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37271      */
37272     maxHeight: 300,
37273     /**
37274      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37275      * query specified by the allQuery config option (defaults to 'query')
37276      */
37277     triggerAction: 'query',
37278     /**
37279      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37280      * (defaults to 4, does not apply if editable = false)
37281      */
37282     minChars : 4,
37283     /**
37284      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37285      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37286      */
37287     typeAhead: false,
37288     /**
37289      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37290      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37291      */
37292     queryDelay: 500,
37293     /**
37294      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37295      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37296      */
37297     pageSize: 0,
37298     /**
37299      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37300      * when editable = true (defaults to false)
37301      */
37302     selectOnFocus:false,
37303     /**
37304      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37305      */
37306     queryParam: 'query',
37307     /**
37308      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37309      * when mode = 'remote' (defaults to 'Loading...')
37310      */
37311     loadingText: 'Loading...',
37312     /**
37313      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37314      */
37315     resizable: false,
37316     /**
37317      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37318      */
37319     handleHeight : 8,
37320     /**
37321      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37322      * traditional select (defaults to true)
37323      */
37324     editable: true,
37325     /**
37326      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37327      */
37328     allQuery: '',
37329     /**
37330      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37331      */
37332     mode: 'remote',
37333     /**
37334      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37335      * listWidth has a higher value)
37336      */
37337     minListWidth : 70,
37338     /**
37339      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37340      * allow the user to set arbitrary text into the field (defaults to false)
37341      */
37342     forceSelection:false,
37343     /**
37344      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37345      * if typeAhead = true (defaults to 250)
37346      */
37347     typeAheadDelay : 250,
37348     /**
37349      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37350      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37351      */
37352     valueNotFoundText : undefined,
37353     /**
37354      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37355      */
37356     blockFocus : false,
37357     
37358     /**
37359      * @cfg {Boolean} disableClear Disable showing of clear button.
37360      */
37361     disableClear : false,
37362     /**
37363      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37364      */
37365     alwaysQuery : false,
37366     
37367     //private
37368     addicon : false,
37369     editicon: false,
37370     
37371     
37372     // private
37373     onRender : function(ct, position){
37374         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37375         if(this.hiddenName){
37376             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37377                     'before', true);
37378             this.hiddenField.value =
37379                 this.hiddenValue !== undefined ? this.hiddenValue :
37380                 this.value !== undefined ? this.value : '';
37381
37382             // prevent input submission
37383             this.el.dom.removeAttribute('name');
37384         }
37385         if(Roo.isGecko){
37386             this.el.dom.setAttribute('autocomplete', 'off');
37387         }
37388
37389         var cls = 'x-combo-list';
37390
37391         this.list = new Roo.Layer({
37392             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37393         });
37394
37395         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37396         this.list.setWidth(lw);
37397         this.list.swallowEvent('mousewheel');
37398         this.assetHeight = 0;
37399
37400         if(this.title){
37401             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37402             this.assetHeight += this.header.getHeight();
37403         }
37404
37405         this.innerList = this.list.createChild({cls:cls+'-inner'});
37406         this.innerList.on('mouseover', this.onViewOver, this);
37407         this.innerList.on('mousemove', this.onViewMove, this);
37408         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37409         
37410         if(this.allowBlank && !this.pageSize && !this.disableClear){
37411             this.footer = this.list.createChild({cls:cls+'-ft'});
37412             this.pageTb = new Roo.Toolbar(this.footer);
37413            
37414         }
37415         if(this.pageSize){
37416             this.footer = this.list.createChild({cls:cls+'-ft'});
37417             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37418                     {pageSize: this.pageSize});
37419             
37420         }
37421         
37422         if (this.pageTb && this.allowBlank && !this.disableClear) {
37423             var _this = this;
37424             this.pageTb.add(new Roo.Toolbar.Fill(), {
37425                 cls: 'x-btn-icon x-btn-clear',
37426                 text: '&#160;',
37427                 handler: function()
37428                 {
37429                     _this.collapse();
37430                     _this.clearValue();
37431                     _this.onSelect(false, -1);
37432                 }
37433             });
37434         }
37435         if (this.footer) {
37436             this.assetHeight += this.footer.getHeight();
37437         }
37438         
37439
37440         if(!this.tpl){
37441             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37442         }
37443
37444         this.view = new Roo.View(this.innerList, this.tpl, {
37445             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37446         });
37447
37448         this.view.on('click', this.onViewClick, this);
37449
37450         this.store.on('beforeload', this.onBeforeLoad, this);
37451         this.store.on('load', this.onLoad, this);
37452         this.store.on('loadexception', this.collapse, this);
37453
37454         if(this.resizable){
37455             this.resizer = new Roo.Resizable(this.list,  {
37456                pinned:true, handles:'se'
37457             });
37458             this.resizer.on('resize', function(r, w, h){
37459                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37460                 this.listWidth = w;
37461                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37462                 this.restrictHeight();
37463             }, this);
37464             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37465         }
37466         if(!this.editable){
37467             this.editable = true;
37468             this.setEditable(false);
37469         }  
37470         
37471         
37472         if (typeof(this.events.add.listeners) != 'undefined') {
37473             
37474             this.addicon = this.wrap.createChild(
37475                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37476        
37477             this.addicon.on('click', function(e) {
37478                 this.fireEvent('add', this);
37479             }, this);
37480         }
37481         if (typeof(this.events.edit.listeners) != 'undefined') {
37482             
37483             this.editicon = this.wrap.createChild(
37484                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37485             if (this.addicon) {
37486                 this.editicon.setStyle('margin-left', '40px');
37487             }
37488             this.editicon.on('click', function(e) {
37489                 
37490                 // we fire even  if inothing is selected..
37491                 this.fireEvent('edit', this, this.lastData );
37492                 
37493             }, this);
37494         }
37495         
37496         
37497         
37498     },
37499
37500     // private
37501     initEvents : function(){
37502         Roo.form.ComboBox.superclass.initEvents.call(this);
37503
37504         this.keyNav = new Roo.KeyNav(this.el, {
37505             "up" : function(e){
37506                 this.inKeyMode = true;
37507                 this.selectPrev();
37508             },
37509
37510             "down" : function(e){
37511                 if(!this.isExpanded()){
37512                     this.onTriggerClick();
37513                 }else{
37514                     this.inKeyMode = true;
37515                     this.selectNext();
37516                 }
37517             },
37518
37519             "enter" : function(e){
37520                 this.onViewClick();
37521                 //return true;
37522             },
37523
37524             "esc" : function(e){
37525                 this.collapse();
37526             },
37527
37528             "tab" : function(e){
37529                 this.onViewClick(false);
37530                 return true;
37531             },
37532
37533             scope : this,
37534
37535             doRelay : function(foo, bar, hname){
37536                 if(hname == 'down' || this.scope.isExpanded()){
37537                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37538                 }
37539                 return true;
37540             },
37541
37542             forceKeyDown: true
37543         });
37544         this.queryDelay = Math.max(this.queryDelay || 10,
37545                 this.mode == 'local' ? 10 : 250);
37546         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37547         if(this.typeAhead){
37548             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37549         }
37550         if(this.editable !== false){
37551             this.el.on("keyup", this.onKeyUp, this);
37552         }
37553         if(this.forceSelection){
37554             this.on('blur', this.doForce, this);
37555         }
37556     },
37557
37558     onDestroy : function(){
37559         if(this.view){
37560             this.view.setStore(null);
37561             this.view.el.removeAllListeners();
37562             this.view.el.remove();
37563             this.view.purgeListeners();
37564         }
37565         if(this.list){
37566             this.list.destroy();
37567         }
37568         if(this.store){
37569             this.store.un('beforeload', this.onBeforeLoad, this);
37570             this.store.un('load', this.onLoad, this);
37571             this.store.un('loadexception', this.collapse, this);
37572         }
37573         Roo.form.ComboBox.superclass.onDestroy.call(this);
37574     },
37575
37576     // private
37577     fireKey : function(e){
37578         if(e.isNavKeyPress() && !this.list.isVisible()){
37579             this.fireEvent("specialkey", this, e);
37580         }
37581     },
37582
37583     // private
37584     onResize: function(w, h){
37585         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37586         
37587         if(typeof w != 'number'){
37588             // we do not handle it!?!?
37589             return;
37590         }
37591         var tw = this.trigger.getWidth();
37592         tw += this.addicon ? this.addicon.getWidth() : 0;
37593         tw += this.editicon ? this.editicon.getWidth() : 0;
37594         var x = w - tw;
37595         this.el.setWidth( this.adjustWidth('input', x));
37596             
37597         this.trigger.setStyle('left', x+'px');
37598         
37599         if(this.list && this.listWidth === undefined){
37600             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37601             this.list.setWidth(lw);
37602             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37603         }
37604         
37605     
37606         
37607     },
37608
37609     /**
37610      * Allow or prevent the user from directly editing the field text.  If false is passed,
37611      * the user will only be able to select from the items defined in the dropdown list.  This method
37612      * is the runtime equivalent of setting the 'editable' config option at config time.
37613      * @param {Boolean} value True to allow the user to directly edit the field text
37614      */
37615     setEditable : function(value){
37616         if(value == this.editable){
37617             return;
37618         }
37619         this.editable = value;
37620         if(!value){
37621             this.el.dom.setAttribute('readOnly', true);
37622             this.el.on('mousedown', this.onTriggerClick,  this);
37623             this.el.addClass('x-combo-noedit');
37624         }else{
37625             this.el.dom.setAttribute('readOnly', false);
37626             this.el.un('mousedown', this.onTriggerClick,  this);
37627             this.el.removeClass('x-combo-noedit');
37628         }
37629     },
37630
37631     // private
37632     onBeforeLoad : function(){
37633         if(!this.hasFocus){
37634             return;
37635         }
37636         this.innerList.update(this.loadingText ?
37637                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37638         this.restrictHeight();
37639         this.selectedIndex = -1;
37640     },
37641
37642     // private
37643     onLoad : function(){
37644         if(!this.hasFocus){
37645             return;
37646         }
37647         if(this.store.getCount() > 0){
37648             this.expand();
37649             this.restrictHeight();
37650             if(this.lastQuery == this.allQuery){
37651                 if(this.editable){
37652                     this.el.dom.select();
37653                 }
37654                 if(!this.selectByValue(this.value, true)){
37655                     this.select(0, true);
37656                 }
37657             }else{
37658                 this.selectNext();
37659                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37660                     this.taTask.delay(this.typeAheadDelay);
37661                 }
37662             }
37663         }else{
37664             this.onEmptyResults();
37665         }
37666         //this.el.focus();
37667     },
37668
37669     // private
37670     onTypeAhead : function(){
37671         if(this.store.getCount() > 0){
37672             var r = this.store.getAt(0);
37673             var newValue = r.data[this.displayField];
37674             var len = newValue.length;
37675             var selStart = this.getRawValue().length;
37676             if(selStart != len){
37677                 this.setRawValue(newValue);
37678                 this.selectText(selStart, newValue.length);
37679             }
37680         }
37681     },
37682
37683     // private
37684     onSelect : function(record, index){
37685         if(this.fireEvent('beforeselect', this, record, index) !== false){
37686             this.setFromData(index > -1 ? record.data : false);
37687             this.collapse();
37688             this.fireEvent('select', this, record, index);
37689         }
37690     },
37691
37692     /**
37693      * Returns the currently selected field value or empty string if no value is set.
37694      * @return {String} value The selected value
37695      */
37696     getValue : function(){
37697         if(this.valueField){
37698             return typeof this.value != 'undefined' ? this.value : '';
37699         }else{
37700             return Roo.form.ComboBox.superclass.getValue.call(this);
37701         }
37702     },
37703
37704     /**
37705      * Clears any text/value currently set in the field
37706      */
37707     clearValue : function(){
37708         if(this.hiddenField){
37709             this.hiddenField.value = '';
37710         }
37711         this.value = '';
37712         this.setRawValue('');
37713         this.lastSelectionText = '';
37714         this.applyEmptyText();
37715     },
37716
37717     /**
37718      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37719      * will be displayed in the field.  If the value does not match the data value of an existing item,
37720      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37721      * Otherwise the field will be blank (although the value will still be set).
37722      * @param {String} value The value to match
37723      */
37724     setValue : function(v){
37725         var text = v;
37726         if(this.valueField){
37727             var r = this.findRecord(this.valueField, v);
37728             if(r){
37729                 text = r.data[this.displayField];
37730             }else if(this.valueNotFoundText !== undefined){
37731                 text = this.valueNotFoundText;
37732             }
37733         }
37734         this.lastSelectionText = text;
37735         if(this.hiddenField){
37736             this.hiddenField.value = v;
37737         }
37738         Roo.form.ComboBox.superclass.setValue.call(this, text);
37739         this.value = v;
37740     },
37741     /**
37742      * @property {Object} the last set data for the element
37743      */
37744     
37745     lastData : false,
37746     /**
37747      * Sets the value of the field based on a object which is related to the record format for the store.
37748      * @param {Object} value the value to set as. or false on reset?
37749      */
37750     setFromData : function(o){
37751         var dv = ''; // display value
37752         var vv = ''; // value value..
37753         this.lastData = o;
37754         if (this.displayField) {
37755             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37756         } else {
37757             // this is an error condition!!!
37758             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37759         }
37760         
37761         if(this.valueField){
37762             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37763         }
37764         if(this.hiddenField){
37765             this.hiddenField.value = vv;
37766             
37767             this.lastSelectionText = dv;
37768             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37769             this.value = vv;
37770             return;
37771         }
37772         // no hidden field.. - we store the value in 'value', but still display
37773         // display field!!!!
37774         this.lastSelectionText = dv;
37775         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37776         this.value = vv;
37777         
37778         
37779     },
37780     // private
37781     reset : function(){
37782         // overridden so that last data is reset..
37783         this.setValue(this.originalValue);
37784         this.clearInvalid();
37785         this.lastData = false;
37786     },
37787     // private
37788     findRecord : function(prop, value){
37789         var record;
37790         if(this.store.getCount() > 0){
37791             this.store.each(function(r){
37792                 if(r.data[prop] == value){
37793                     record = r;
37794                     return false;
37795                 }
37796             });
37797         }
37798         return record;
37799     },
37800
37801     // private
37802     onViewMove : function(e, t){
37803         this.inKeyMode = false;
37804     },
37805
37806     // private
37807     onViewOver : function(e, t){
37808         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37809             return;
37810         }
37811         var item = this.view.findItemFromChild(t);
37812         if(item){
37813             var index = this.view.indexOf(item);
37814             this.select(index, false);
37815         }
37816     },
37817
37818     // private
37819     onViewClick : function(doFocus){
37820         var index = this.view.getSelectedIndexes()[0];
37821         var r = this.store.getAt(index);
37822         if(r){
37823             this.onSelect(r, index);
37824         }
37825         if(doFocus !== false && !this.blockFocus){
37826             this.el.focus();
37827         }
37828     },
37829
37830     // private
37831     restrictHeight : function(){
37832         this.innerList.dom.style.height = '';
37833         var inner = this.innerList.dom;
37834         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37835         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37836         this.list.beginUpdate();
37837         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37838         this.list.alignTo(this.el, this.listAlign);
37839         this.list.endUpdate();
37840     },
37841
37842     // private
37843     onEmptyResults : function(){
37844         this.collapse();
37845     },
37846
37847     /**
37848      * Returns true if the dropdown list is expanded, else false.
37849      */
37850     isExpanded : function(){
37851         return this.list.isVisible();
37852     },
37853
37854     /**
37855      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37856      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37857      * @param {String} value The data value of the item to select
37858      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37859      * selected item if it is not currently in view (defaults to true)
37860      * @return {Boolean} True if the value matched an item in the list, else false
37861      */
37862     selectByValue : function(v, scrollIntoView){
37863         if(v !== undefined && v !== null){
37864             var r = this.findRecord(this.valueField || this.displayField, v);
37865             if(r){
37866                 this.select(this.store.indexOf(r), scrollIntoView);
37867                 return true;
37868             }
37869         }
37870         return false;
37871     },
37872
37873     /**
37874      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37875      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37876      * @param {Number} index The zero-based index of the list item to select
37877      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37878      * selected item if it is not currently in view (defaults to true)
37879      */
37880     select : function(index, scrollIntoView){
37881         this.selectedIndex = index;
37882         this.view.select(index);
37883         if(scrollIntoView !== false){
37884             var el = this.view.getNode(index);
37885             if(el){
37886                 this.innerList.scrollChildIntoView(el, false);
37887             }
37888         }
37889     },
37890
37891     // private
37892     selectNext : function(){
37893         var ct = this.store.getCount();
37894         if(ct > 0){
37895             if(this.selectedIndex == -1){
37896                 this.select(0);
37897             }else if(this.selectedIndex < ct-1){
37898                 this.select(this.selectedIndex+1);
37899             }
37900         }
37901     },
37902
37903     // private
37904     selectPrev : function(){
37905         var ct = this.store.getCount();
37906         if(ct > 0){
37907             if(this.selectedIndex == -1){
37908                 this.select(0);
37909             }else if(this.selectedIndex != 0){
37910                 this.select(this.selectedIndex-1);
37911             }
37912         }
37913     },
37914
37915     // private
37916     onKeyUp : function(e){
37917         if(this.editable !== false && !e.isSpecialKey()){
37918             this.lastKey = e.getKey();
37919             this.dqTask.delay(this.queryDelay);
37920         }
37921     },
37922
37923     // private
37924     validateBlur : function(){
37925         return !this.list || !this.list.isVisible();   
37926     },
37927
37928     // private
37929     initQuery : function(){
37930         this.doQuery(this.getRawValue());
37931     },
37932
37933     // private
37934     doForce : function(){
37935         if(this.el.dom.value.length > 0){
37936             this.el.dom.value =
37937                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37938             this.applyEmptyText();
37939         }
37940     },
37941
37942     /**
37943      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37944      * query allowing the query action to be canceled if needed.
37945      * @param {String} query The SQL query to execute
37946      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37947      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37948      * saved in the current store (defaults to false)
37949      */
37950     doQuery : function(q, forceAll){
37951         if(q === undefined || q === null){
37952             q = '';
37953         }
37954         var qe = {
37955             query: q,
37956             forceAll: forceAll,
37957             combo: this,
37958             cancel:false
37959         };
37960         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37961             return false;
37962         }
37963         q = qe.query;
37964         forceAll = qe.forceAll;
37965         if(forceAll === true || (q.length >= this.minChars)){
37966             if(this.lastQuery != q || this.alwaysQuery){
37967                 this.lastQuery = q;
37968                 if(this.mode == 'local'){
37969                     this.selectedIndex = -1;
37970                     if(forceAll){
37971                         this.store.clearFilter();
37972                     }else{
37973                         this.store.filter(this.displayField, q);
37974                     }
37975                     this.onLoad();
37976                 }else{
37977                     this.store.baseParams[this.queryParam] = q;
37978                     this.store.load({
37979                         params: this.getParams(q)
37980                     });
37981                     this.expand();
37982                 }
37983             }else{
37984                 this.selectedIndex = -1;
37985                 this.onLoad();   
37986             }
37987         }
37988     },
37989
37990     // private
37991     getParams : function(q){
37992         var p = {};
37993         //p[this.queryParam] = q;
37994         if(this.pageSize){
37995             p.start = 0;
37996             p.limit = this.pageSize;
37997         }
37998         return p;
37999     },
38000
38001     /**
38002      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38003      */
38004     collapse : function(){
38005         if(!this.isExpanded()){
38006             return;
38007         }
38008         this.list.hide();
38009         Roo.get(document).un('mousedown', this.collapseIf, this);
38010         Roo.get(document).un('mousewheel', this.collapseIf, this);
38011         if (!this.editable) {
38012             Roo.get(document).un('keydown', this.listKeyPress, this);
38013         }
38014         this.fireEvent('collapse', this);
38015     },
38016
38017     // private
38018     collapseIf : function(e){
38019         if(!e.within(this.wrap) && !e.within(this.list)){
38020             this.collapse();
38021         }
38022     },
38023
38024     /**
38025      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38026      */
38027     expand : function(){
38028         if(this.isExpanded() || !this.hasFocus){
38029             return;
38030         }
38031         this.list.alignTo(this.el, this.listAlign);
38032         this.list.show();
38033         Roo.get(document).on('mousedown', this.collapseIf, this);
38034         Roo.get(document).on('mousewheel', this.collapseIf, this);
38035         if (!this.editable) {
38036             Roo.get(document).on('keydown', this.listKeyPress, this);
38037         }
38038         
38039         this.fireEvent('expand', this);
38040     },
38041
38042     // private
38043     // Implements the default empty TriggerField.onTriggerClick function
38044     onTriggerClick : function(){
38045         if(this.disabled){
38046             return;
38047         }
38048         if(this.isExpanded()){
38049             this.collapse();
38050             if (!this.blockFocus) {
38051                 this.el.focus();
38052             }
38053             
38054         }else {
38055             this.hasFocus = true;
38056             if(this.triggerAction == 'all') {
38057                 this.doQuery(this.allQuery, true);
38058             } else {
38059                 this.doQuery(this.getRawValue());
38060             }
38061             if (!this.blockFocus) {
38062                 this.el.focus();
38063             }
38064         }
38065     },
38066     listKeyPress : function(e)
38067     {
38068         //Roo.log('listkeypress');
38069         // scroll to first matching element based on key pres..
38070         if (e.isSpecialKey()) {
38071             return false;
38072         }
38073         var k = String.fromCharCode(e.getKey()).toUpperCase();
38074         //Roo.log(k);
38075         var match  = false;
38076         var csel = this.view.getSelectedNodes();
38077         var cselitem = false;
38078         if (csel.length) {
38079             var ix = this.view.indexOf(csel[0]);
38080             cselitem  = this.store.getAt(ix);
38081             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38082                 cselitem = false;
38083             }
38084             
38085         }
38086         
38087         this.store.each(function(v) { 
38088             if (cselitem) {
38089                 // start at existing selection.
38090                 if (cselitem.id == v.id) {
38091                     cselitem = false;
38092                 }
38093                 return;
38094             }
38095                 
38096             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38097                 match = this.store.indexOf(v);
38098                 return false;
38099             }
38100         }, this);
38101         
38102         if (match === false) {
38103             return true; // no more action?
38104         }
38105         // scroll to?
38106         this.view.select(match);
38107         var sn = Roo.get(this.view.getSelectedNodes()[0])
38108         sn.scrollIntoView(sn.dom.parentNode, false);
38109     }
38110
38111     /** 
38112     * @cfg {Boolean} grow 
38113     * @hide 
38114     */
38115     /** 
38116     * @cfg {Number} growMin 
38117     * @hide 
38118     */
38119     /** 
38120     * @cfg {Number} growMax 
38121     * @hide 
38122     */
38123     /**
38124      * @hide
38125      * @method autoSize
38126      */
38127 });/*
38128  * Based on:
38129  * Ext JS Library 1.1.1
38130  * Copyright(c) 2006-2007, Ext JS, LLC.
38131  *
38132  * Originally Released Under LGPL - original licence link has changed is not relivant.
38133  *
38134  * Fork - LGPL
38135  * <script type="text/javascript">
38136  */
38137 /**
38138  * @class Roo.form.Checkbox
38139  * @extends Roo.form.Field
38140  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38141  * @constructor
38142  * Creates a new Checkbox
38143  * @param {Object} config Configuration options
38144  */
38145 Roo.form.Checkbox = function(config){
38146     Roo.form.Checkbox.superclass.constructor.call(this, config);
38147     this.addEvents({
38148         /**
38149          * @event check
38150          * Fires when the checkbox is checked or unchecked.
38151              * @param {Roo.form.Checkbox} this This checkbox
38152              * @param {Boolean} checked The new checked value
38153              */
38154         check : true
38155     });
38156 };
38157
38158 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38159     /**
38160      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38161      */
38162     focusClass : undefined,
38163     /**
38164      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38165      */
38166     fieldClass: "x-form-field",
38167     /**
38168      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38169      */
38170     checked: false,
38171     /**
38172      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38173      * {tag: "input", type: "checkbox", autocomplete: "off"})
38174      */
38175     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38176     /**
38177      * @cfg {String} boxLabel The text that appears beside the checkbox
38178      */
38179     boxLabel : "",
38180     /**
38181      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38182      */  
38183     inputValue : '1',
38184     /**
38185      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38186      */
38187      valueOff: '0', // value when not checked..
38188
38189     actionMode : 'viewEl', 
38190     //
38191     // private
38192     itemCls : 'x-menu-check-item x-form-item',
38193     groupClass : 'x-menu-group-item',
38194     inputType : 'hidden',
38195     
38196     
38197     inSetChecked: false, // check that we are not calling self...
38198     
38199     inputElement: false, // real input element?
38200     basedOn: false, // ????
38201     
38202     isFormField: true, // not sure where this is needed!!!!
38203
38204     onResize : function(){
38205         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38206         if(!this.boxLabel){
38207             this.el.alignTo(this.wrap, 'c-c');
38208         }
38209     },
38210
38211     initEvents : function(){
38212         Roo.form.Checkbox.superclass.initEvents.call(this);
38213         this.el.on("click", this.onClick,  this);
38214         this.el.on("change", this.onClick,  this);
38215     },
38216
38217
38218     getResizeEl : function(){
38219         return this.wrap;
38220     },
38221
38222     getPositionEl : function(){
38223         return this.wrap;
38224     },
38225
38226     // private
38227     onRender : function(ct, position){
38228         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38229         /*
38230         if(this.inputValue !== undefined){
38231             this.el.dom.value = this.inputValue;
38232         }
38233         */
38234         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38235         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38236         var viewEl = this.wrap.createChild({ 
38237             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38238         this.viewEl = viewEl;   
38239         this.wrap.on('click', this.onClick,  this); 
38240         
38241         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38242         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38243         
38244         
38245         
38246         if(this.boxLabel){
38247             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38248         //    viewEl.on('click', this.onClick,  this); 
38249         }
38250         //if(this.checked){
38251             this.setChecked(this.checked);
38252         //}else{
38253             //this.checked = this.el.dom;
38254         //}
38255
38256     },
38257
38258     // private
38259     initValue : Roo.emptyFn,
38260
38261     /**
38262      * Returns the checked state of the checkbox.
38263      * @return {Boolean} True if checked, else false
38264      */
38265     getValue : function(){
38266         if(this.el){
38267             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38268         }
38269         return this.valueOff;
38270         
38271     },
38272
38273         // private
38274     onClick : function(){ 
38275         this.setChecked(!this.checked);
38276
38277         //if(this.el.dom.checked != this.checked){
38278         //    this.setValue(this.el.dom.checked);
38279        // }
38280     },
38281
38282     /**
38283      * Sets the checked state of the checkbox.
38284      * On is always based on a string comparison between inputValue and the param.
38285      * @param {Boolean/String} value - the value to set 
38286      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38287      */
38288     setValue : function(v,suppressEvent){
38289         
38290         
38291         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38292         //if(this.el && this.el.dom){
38293         //    this.el.dom.checked = this.checked;
38294         //    this.el.dom.defaultChecked = this.checked;
38295         //}
38296         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38297         //this.fireEvent("check", this, this.checked);
38298     },
38299     // private..
38300     setChecked : function(state,suppressEvent)
38301     {
38302         if (this.inSetChecked) {
38303             this.checked = state;
38304             return;
38305         }
38306         
38307     
38308         if(this.wrap){
38309             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38310         }
38311         this.checked = state;
38312         if(suppressEvent !== true){
38313             this.fireEvent('check', this, state);
38314         }
38315         this.inSetChecked = true;
38316         this.el.dom.value = state ? this.inputValue : this.valueOff;
38317         this.inSetChecked = false;
38318         
38319     },
38320     // handle setting of hidden value by some other method!!?!?
38321     setFromHidden: function()
38322     {
38323         if(!this.el){
38324             return;
38325         }
38326         //console.log("SET FROM HIDDEN");
38327         //alert('setFrom hidden');
38328         this.setValue(this.el.dom.value);
38329     },
38330     
38331     onDestroy : function()
38332     {
38333         if(this.viewEl){
38334             Roo.get(this.viewEl).remove();
38335         }
38336          
38337         Roo.form.Checkbox.superclass.onDestroy.call(this);
38338     }
38339
38340 });/*
38341  * Based on:
38342  * Ext JS Library 1.1.1
38343  * Copyright(c) 2006-2007, Ext JS, LLC.
38344  *
38345  * Originally Released Under LGPL - original licence link has changed is not relivant.
38346  *
38347  * Fork - LGPL
38348  * <script type="text/javascript">
38349  */
38350  
38351 /**
38352  * @class Roo.form.Radio
38353  * @extends Roo.form.Checkbox
38354  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38355  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38356  * @constructor
38357  * Creates a new Radio
38358  * @param {Object} config Configuration options
38359  */
38360 Roo.form.Radio = function(){
38361     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38362 };
38363 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38364     inputType: 'radio',
38365
38366     /**
38367      * If this radio is part of a group, it will return the selected value
38368      * @return {String}
38369      */
38370     getGroupValue : function(){
38371         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38372     }
38373 });//<script type="text/javascript">
38374
38375 /*
38376  * Ext JS Library 1.1.1
38377  * Copyright(c) 2006-2007, Ext JS, LLC.
38378  * licensing@extjs.com
38379  * 
38380  * http://www.extjs.com/license
38381  */
38382  
38383  /*
38384   * 
38385   * Known bugs:
38386   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38387   * - IE ? - no idea how much works there.
38388   * 
38389   * 
38390   * 
38391   */
38392  
38393
38394 /**
38395  * @class Ext.form.HtmlEditor
38396  * @extends Ext.form.Field
38397  * Provides a lightweight HTML Editor component.
38398  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38399  * 
38400  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38401  * supported by this editor.</b><br/><br/>
38402  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38403  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38404  */
38405 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38406       /**
38407      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38408      */
38409     toolbars : false,
38410     /**
38411      * @cfg {String} createLinkText The default text for the create link prompt
38412      */
38413     createLinkText : 'Please enter the URL for the link:',
38414     /**
38415      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38416      */
38417     defaultLinkValue : 'http:/'+'/',
38418    
38419     
38420     // id of frame..
38421     frameId: false,
38422     
38423     // private properties
38424     validationEvent : false,
38425     deferHeight: true,
38426     initialized : false,
38427     activated : false,
38428     sourceEditMode : false,
38429     onFocus : Roo.emptyFn,
38430     iframePad:3,
38431     hideMode:'offsets',
38432     defaultAutoCreate : {
38433         tag: "textarea",
38434         style:"width:500px;height:300px;",
38435         autocomplete: "off"
38436     },
38437
38438     // private
38439     initComponent : function(){
38440         this.addEvents({
38441             /**
38442              * @event initialize
38443              * Fires when the editor is fully initialized (including the iframe)
38444              * @param {HtmlEditor} this
38445              */
38446             initialize: true,
38447             /**
38448              * @event activate
38449              * Fires when the editor is first receives the focus. Any insertion must wait
38450              * until after this event.
38451              * @param {HtmlEditor} this
38452              */
38453             activate: true,
38454              /**
38455              * @event beforesync
38456              * Fires before the textarea is updated with content from the editor iframe. Return false
38457              * to cancel the sync.
38458              * @param {HtmlEditor} this
38459              * @param {String} html
38460              */
38461             beforesync: true,
38462              /**
38463              * @event beforepush
38464              * Fires before the iframe editor is updated with content from the textarea. Return false
38465              * to cancel the push.
38466              * @param {HtmlEditor} this
38467              * @param {String} html
38468              */
38469             beforepush: true,
38470              /**
38471              * @event sync
38472              * Fires when the textarea is updated with content from the editor iframe.
38473              * @param {HtmlEditor} this
38474              * @param {String} html
38475              */
38476             sync: true,
38477              /**
38478              * @event push
38479              * Fires when the iframe editor is updated with content from the textarea.
38480              * @param {HtmlEditor} this
38481              * @param {String} html
38482              */
38483             push: true,
38484              /**
38485              * @event editmodechange
38486              * Fires when the editor switches edit modes
38487              * @param {HtmlEditor} this
38488              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38489              */
38490             editmodechange: true,
38491             /**
38492              * @event editorevent
38493              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38494              * @param {HtmlEditor} this
38495              */
38496             editorevent: true
38497         })
38498     },
38499
38500     /**
38501      * Protected method that will not generally be called directly. It
38502      * is called when the editor creates its toolbar. Override this method if you need to
38503      * add custom toolbar buttons.
38504      * @param {HtmlEditor} editor
38505      */
38506     createToolbar : function(editor){
38507         if (!editor.toolbars || !editor.toolbars.length) {
38508             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38509         }
38510         
38511         for (var i =0 ; i < editor.toolbars.length;i++) {
38512             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38513             editor.toolbars[i].init(editor);
38514         }
38515          
38516         
38517     },
38518
38519     /**
38520      * Protected method that will not generally be called directly. It
38521      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38522      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38523      */
38524     getDocMarkup : function(){
38525         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
38526     },
38527
38528     // private
38529     onRender : function(ct, position){
38530         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38531         this.el.dom.style.border = '0 none';
38532         this.el.dom.setAttribute('tabIndex', -1);
38533         this.el.addClass('x-hidden');
38534         if(Roo.isIE){ // fix IE 1px bogus margin
38535             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38536         }
38537         this.wrap = this.el.wrap({
38538             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38539         });
38540
38541         this.frameId = Roo.id();
38542         this.createToolbar(this);
38543         
38544         
38545         
38546         
38547       
38548         
38549         var iframe = this.wrap.createChild({
38550             tag: 'iframe',
38551             id: this.frameId,
38552             name: this.frameId,
38553             frameBorder : 'no',
38554             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38555         });
38556         
38557        // console.log(iframe);
38558         //this.wrap.dom.appendChild(iframe);
38559
38560         this.iframe = iframe.dom;
38561
38562          this.assignDocWin();
38563         
38564         this.doc.designMode = 'on';
38565        
38566         this.doc.open();
38567         this.doc.write(this.getDocMarkup());
38568         this.doc.close();
38569
38570         
38571         var task = { // must defer to wait for browser to be ready
38572             run : function(){
38573                 //console.log("run task?" + this.doc.readyState);
38574                 this.assignDocWin();
38575                 if(this.doc.body || this.doc.readyState == 'complete'){
38576                     try {
38577                         this.doc.designMode="on";
38578                     } catch (e) {
38579                         return;
38580                     }
38581                     Roo.TaskMgr.stop(task);
38582                     this.initEditor.defer(10, this);
38583                 }
38584             },
38585             interval : 10,
38586             duration:10000,
38587             scope: this
38588         };
38589         Roo.TaskMgr.start(task);
38590
38591         if(!this.width){
38592             this.setSize(this.el.getSize());
38593         }
38594     },
38595
38596     // private
38597     onResize : function(w, h){
38598         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38599         if(this.el && this.iframe){
38600             if(typeof w == 'number'){
38601                 var aw = w - this.wrap.getFrameWidth('lr');
38602                 this.el.setWidth(this.adjustWidth('textarea', aw));
38603                 this.iframe.style.width = aw + 'px';
38604             }
38605             if(typeof h == 'number'){
38606                 var tbh = 0;
38607                 for (var i =0; i < this.toolbars.length;i++) {
38608                     // fixme - ask toolbars for heights?
38609                     tbh += this.toolbars[i].tb.el.getHeight();
38610                 }
38611                 
38612                 
38613                 
38614                 
38615                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38616                 this.el.setHeight(this.adjustWidth('textarea', ah));
38617                 this.iframe.style.height = ah + 'px';
38618                 if(this.doc){
38619                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38620                 }
38621             }
38622         }
38623     },
38624
38625     /**
38626      * Toggles the editor between standard and source edit mode.
38627      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38628      */
38629     toggleSourceEdit : function(sourceEditMode){
38630         
38631         this.sourceEditMode = sourceEditMode === true;
38632         
38633         if(this.sourceEditMode){
38634           
38635             this.syncValue();
38636             this.iframe.className = 'x-hidden';
38637             this.el.removeClass('x-hidden');
38638             this.el.dom.removeAttribute('tabIndex');
38639             this.el.focus();
38640         }else{
38641              
38642             this.pushValue();
38643             this.iframe.className = '';
38644             this.el.addClass('x-hidden');
38645             this.el.dom.setAttribute('tabIndex', -1);
38646             this.deferFocus();
38647         }
38648         this.setSize(this.wrap.getSize());
38649         this.fireEvent('editmodechange', this, this.sourceEditMode);
38650     },
38651
38652     // private used internally
38653     createLink : function(){
38654         var url = prompt(this.createLinkText, this.defaultLinkValue);
38655         if(url && url != 'http:/'+'/'){
38656             this.relayCmd('createlink', url);
38657         }
38658     },
38659
38660     // private (for BoxComponent)
38661     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38662
38663     // private (for BoxComponent)
38664     getResizeEl : function(){
38665         return this.wrap;
38666     },
38667
38668     // private (for BoxComponent)
38669     getPositionEl : function(){
38670         return this.wrap;
38671     },
38672
38673     // private
38674     initEvents : function(){
38675         this.originalValue = this.getValue();
38676     },
38677
38678     /**
38679      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38680      * @method
38681      */
38682     markInvalid : Roo.emptyFn,
38683     /**
38684      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38685      * @method
38686      */
38687     clearInvalid : Roo.emptyFn,
38688
38689     setValue : function(v){
38690         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38691         this.pushValue();
38692     },
38693
38694     /**
38695      * Protected method that will not generally be called directly. If you need/want
38696      * custom HTML cleanup, this is the method you should override.
38697      * @param {String} html The HTML to be cleaned
38698      * return {String} The cleaned HTML
38699      */
38700     cleanHtml : function(html){
38701         html = String(html);
38702         if(html.length > 5){
38703             if(Roo.isSafari){ // strip safari nonsense
38704                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38705             }
38706         }
38707         if(html == '&nbsp;'){
38708             html = '';
38709         }
38710         return html;
38711     },
38712
38713     /**
38714      * Protected method that will not generally be called directly. Syncs the contents
38715      * of the editor iframe with the textarea.
38716      */
38717     syncValue : function(){
38718         if(this.initialized){
38719             var bd = (this.doc.body || this.doc.documentElement);
38720             this.cleanUpPaste();
38721             var html = bd.innerHTML;
38722             if(Roo.isSafari){
38723                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38724                 var m = bs.match(/text-align:(.*?);/i);
38725                 if(m && m[1]){
38726                     html = '<div style="'+m[0]+'">' + html + '</div>';
38727                 }
38728             }
38729             html = this.cleanHtml(html);
38730             if(this.fireEvent('beforesync', this, html) !== false){
38731                 this.el.dom.value = html;
38732                 this.fireEvent('sync', this, html);
38733             }
38734         }
38735     },
38736
38737     /**
38738      * Protected method that will not generally be called directly. Pushes the value of the textarea
38739      * into the iframe editor.
38740      */
38741     pushValue : function(){
38742         if(this.initialized){
38743             var v = this.el.dom.value;
38744             if(v.length < 1){
38745                 v = '&#160;';
38746             }
38747             
38748             if(this.fireEvent('beforepush', this, v) !== false){
38749                 var d = (this.doc.body || this.doc.documentElement);
38750                 d.innerHTML = v;
38751                 this.cleanUpPaste();
38752                 this.el.dom.value = d.innerHTML;
38753                 this.fireEvent('push', this, v);
38754             }
38755         }
38756     },
38757
38758     // private
38759     deferFocus : function(){
38760         this.focus.defer(10, this);
38761     },
38762
38763     // doc'ed in Field
38764     focus : function(){
38765         if(this.win && !this.sourceEditMode){
38766             this.win.focus();
38767         }else{
38768             this.el.focus();
38769         }
38770     },
38771     
38772     assignDocWin: function()
38773     {
38774         var iframe = this.iframe;
38775         
38776          if(Roo.isIE){
38777             this.doc = iframe.contentWindow.document;
38778             this.win = iframe.contentWindow;
38779         } else {
38780             if (!Roo.get(this.frameId)) {
38781                 return;
38782             }
38783             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38784             this.win = Roo.get(this.frameId).dom.contentWindow;
38785         }
38786     },
38787     
38788     // private
38789     initEditor : function(){
38790         //console.log("INIT EDITOR");
38791         this.assignDocWin();
38792         
38793         
38794         
38795         this.doc.designMode="on";
38796         this.doc.open();
38797         this.doc.write(this.getDocMarkup());
38798         this.doc.close();
38799         
38800         var dbody = (this.doc.body || this.doc.documentElement);
38801         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38802         // this copies styles from the containing element into thsi one..
38803         // not sure why we need all of this..
38804         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38805         ss['background-attachment'] = 'fixed'; // w3c
38806         dbody.bgProperties = 'fixed'; // ie
38807         Roo.DomHelper.applyStyles(dbody, ss);
38808         Roo.EventManager.on(this.doc, {
38809             'mousedown': this.onEditorEvent,
38810             'dblclick': this.onEditorEvent,
38811             'click': this.onEditorEvent,
38812             'keyup': this.onEditorEvent,
38813             buffer:100,
38814             scope: this
38815         });
38816         if(Roo.isGecko){
38817             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
38818         }
38819         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38820             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38821         }
38822         this.initialized = true;
38823
38824         this.fireEvent('initialize', this);
38825         this.pushValue();
38826     },
38827
38828     // private
38829     onDestroy : function(){
38830         
38831         
38832         
38833         if(this.rendered){
38834             
38835             for (var i =0; i < this.toolbars.length;i++) {
38836                 // fixme - ask toolbars for heights?
38837                 this.toolbars[i].onDestroy();
38838             }
38839             
38840             this.wrap.dom.innerHTML = '';
38841             this.wrap.remove();
38842         }
38843     },
38844
38845     // private
38846     onFirstFocus : function(){
38847         
38848         this.assignDocWin();
38849         
38850         
38851         this.activated = true;
38852         for (var i =0; i < this.toolbars.length;i++) {
38853             this.toolbars[i].onFirstFocus();
38854         }
38855        
38856         if(Roo.isGecko){ // prevent silly gecko errors
38857             this.win.focus();
38858             var s = this.win.getSelection();
38859             if(!s.focusNode || s.focusNode.nodeType != 3){
38860                 var r = s.getRangeAt(0);
38861                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38862                 r.collapse(true);
38863                 this.deferFocus();
38864             }
38865             try{
38866                 this.execCmd('useCSS', true);
38867                 this.execCmd('styleWithCSS', false);
38868             }catch(e){}
38869         }
38870         this.fireEvent('activate', this);
38871     },
38872
38873     // private
38874     adjustFont: function(btn){
38875         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38876         //if(Roo.isSafari){ // safari
38877         //    adjust *= 2;
38878        // }
38879         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38880         if(Roo.isSafari){ // safari
38881             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38882             v =  (v < 10) ? 10 : v;
38883             v =  (v > 48) ? 48 : v;
38884             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38885             
38886         }
38887         
38888         
38889         v = Math.max(1, v+adjust);
38890         
38891         this.execCmd('FontSize', v  );
38892     },
38893
38894     onEditorEvent : function(e){
38895         this.fireEvent('editorevent', this, e);
38896       //  this.updateToolbar();
38897         this.syncValue();
38898     },
38899
38900     insertTag : function(tg)
38901     {
38902         // could be a bit smarter... -> wrap the current selected tRoo..
38903         
38904         this.execCmd("formatblock",   tg);
38905         
38906     },
38907     
38908     insertText : function(txt)
38909     {
38910         
38911         
38912         range = this.createRange();
38913         range.deleteContents();
38914                //alert(Sender.getAttribute('label'));
38915                
38916         range.insertNode(this.doc.createTextNode(txt));
38917     } ,
38918     
38919     // private
38920     relayBtnCmd : function(btn){
38921         this.relayCmd(btn.cmd);
38922     },
38923
38924     /**
38925      * Executes a Midas editor command on the editor document and performs necessary focus and
38926      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
38927      * @param {String} cmd The Midas command
38928      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38929      */
38930     relayCmd : function(cmd, value){
38931         this.win.focus();
38932         this.execCmd(cmd, value);
38933         this.fireEvent('editorevent', this);
38934         //this.updateToolbar();
38935         this.deferFocus();
38936     },
38937
38938     /**
38939      * Executes a Midas editor command directly on the editor document.
38940      * For visual commands, you should use {@link #relayCmd} instead.
38941      * <b>This should only be called after the editor is initialized.</b>
38942      * @param {String} cmd The Midas command
38943      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38944      */
38945     execCmd : function(cmd, value){
38946         this.doc.execCommand(cmd, false, value === undefined ? null : value);
38947         this.syncValue();
38948     },
38949
38950    
38951     /**
38952      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
38953      * to insert tRoo.
38954      * @param {String} text
38955      */
38956     insertAtCursor : function(text){
38957         if(!this.activated){
38958             return;
38959         }
38960         if(Roo.isIE){
38961             this.win.focus();
38962             var r = this.doc.selection.createRange();
38963             if(r){
38964                 r.collapse(true);
38965                 r.pasteHTML(text);
38966                 this.syncValue();
38967                 this.deferFocus();
38968             }
38969         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
38970             this.win.focus();
38971             this.execCmd('InsertHTML', text);
38972             this.deferFocus();
38973         }
38974     },
38975  // private
38976     mozKeyPress : function(e){
38977         if(e.ctrlKey){
38978             var c = e.getCharCode(), cmd;
38979           
38980             if(c > 0){
38981                 c = String.fromCharCode(c).toLowerCase();
38982                 switch(c){
38983                     case 'b':
38984                         cmd = 'bold';
38985                     break;
38986                     case 'i':
38987                         cmd = 'italic';
38988                     break;
38989                     case 'u':
38990                         cmd = 'underline';
38991                     case 'v':
38992                         this.cleanUpPaste.defer(100, this);
38993                         return;
38994                     break;
38995                 }
38996                 if(cmd){
38997                     this.win.focus();
38998                     this.execCmd(cmd);
38999                     this.deferFocus();
39000                     e.preventDefault();
39001                 }
39002                 
39003             }
39004         }
39005     },
39006
39007     // private
39008     fixKeys : function(){ // load time branching for fastest keydown performance
39009         if(Roo.isIE){
39010             return function(e){
39011                 var k = e.getKey(), r;
39012                 if(k == e.TAB){
39013                     e.stopEvent();
39014                     r = this.doc.selection.createRange();
39015                     if(r){
39016                         r.collapse(true);
39017                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39018                         this.deferFocus();
39019                     }
39020                     return;
39021                 }
39022                 
39023                 if(k == e.ENTER){
39024                     r = this.doc.selection.createRange();
39025                     if(r){
39026                         var target = r.parentElement();
39027                         if(!target || target.tagName.toLowerCase() != 'li'){
39028                             e.stopEvent();
39029                             r.pasteHTML('<br />');
39030                             r.collapse(false);
39031                             r.select();
39032                         }
39033                     }
39034                 }
39035                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39036                     this.cleanUpPaste.defer(100, this);
39037                     return;
39038                 }
39039                 
39040                 
39041             };
39042         }else if(Roo.isOpera){
39043             return function(e){
39044                 var k = e.getKey();
39045                 if(k == e.TAB){
39046                     e.stopEvent();
39047                     this.win.focus();
39048                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39049                     this.deferFocus();
39050                 }
39051                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39052                     this.cleanUpPaste.defer(100, this);
39053                     return;
39054                 }
39055                 
39056             };
39057         }else if(Roo.isSafari){
39058             return function(e){
39059                 var k = e.getKey();
39060                 
39061                 if(k == e.TAB){
39062                     e.stopEvent();
39063                     this.execCmd('InsertText','\t');
39064                     this.deferFocus();
39065                     return;
39066                 }
39067                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39068                     this.cleanUpPaste.defer(100, this);
39069                     return;
39070                 }
39071                 
39072              };
39073         }
39074     }(),
39075     
39076     getAllAncestors: function()
39077     {
39078         var p = this.getSelectedNode();
39079         var a = [];
39080         if (!p) {
39081             a.push(p); // push blank onto stack..
39082             p = this.getParentElement();
39083         }
39084         
39085         
39086         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39087             a.push(p);
39088             p = p.parentNode;
39089         }
39090         a.push(this.doc.body);
39091         return a;
39092     },
39093     lastSel : false,
39094     lastSelNode : false,
39095     
39096     
39097     getSelection : function() 
39098     {
39099         this.assignDocWin();
39100         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39101     },
39102     
39103     getSelectedNode: function() 
39104     {
39105         // this may only work on Gecko!!!
39106         
39107         // should we cache this!!!!
39108         
39109         
39110         
39111          
39112         var range = this.createRange(this.getSelection());
39113         
39114         if (Roo.isIE) {
39115             var parent = range.parentElement();
39116             while (true) {
39117                 var testRange = range.duplicate();
39118                 testRange.moveToElementText(parent);
39119                 if (testRange.inRange(range)) {
39120                     break;
39121                 }
39122                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39123                     break;
39124                 }
39125                 parent = parent.parentElement;
39126             }
39127             return parent;
39128         }
39129         
39130         
39131         var ar = range.endContainer.childNodes;
39132         if (!ar.length) {
39133             ar = range.commonAncestorContainer.childNodes;
39134             //alert(ar.length);
39135         }
39136         var nodes = [];
39137         var other_nodes = [];
39138         var has_other_nodes = false;
39139         for (var i=0;i<ar.length;i++) {
39140             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39141                 continue;
39142             }
39143             // fullly contained node.
39144             
39145             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39146                 nodes.push(ar[i]);
39147                 continue;
39148             }
39149             
39150             // probably selected..
39151             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39152                 other_nodes.push(ar[i]);
39153                 continue;
39154             }
39155             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39156                 continue;
39157             }
39158             
39159             
39160             has_other_nodes = true;
39161         }
39162         if (!nodes.length && other_nodes.length) {
39163             nodes= other_nodes;
39164         }
39165         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39166             return false;
39167         }
39168         
39169         return nodes[0];
39170     },
39171     createRange: function(sel)
39172     {
39173         // this has strange effects when using with 
39174         // top toolbar - not sure if it's a great idea.
39175         //this.editor.contentWindow.focus();
39176         if (typeof sel != "undefined") {
39177             try {
39178                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39179             } catch(e) {
39180                 return this.doc.createRange();
39181             }
39182         } else {
39183             return this.doc.createRange();
39184         }
39185     },
39186     getParentElement: function()
39187     {
39188         
39189         this.assignDocWin();
39190         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39191         
39192         var range = this.createRange(sel);
39193          
39194         try {
39195             var p = range.commonAncestorContainer;
39196             while (p.nodeType == 3) { // text node
39197                 p = p.parentNode;
39198             }
39199             return p;
39200         } catch (e) {
39201             return null;
39202         }
39203     
39204     },
39205     
39206     
39207     
39208     // BC Hacks - cause I cant work out what i was trying to do..
39209     rangeIntersectsNode : function(range, node)
39210     {
39211         var nodeRange = node.ownerDocument.createRange();
39212         try {
39213             nodeRange.selectNode(node);
39214         }
39215         catch (e) {
39216             nodeRange.selectNodeContents(node);
39217         }
39218
39219         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
39220                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
39221     },
39222     rangeCompareNode : function(range, node) {
39223         var nodeRange = node.ownerDocument.createRange();
39224         try {
39225             nodeRange.selectNode(node);
39226         } catch (e) {
39227             nodeRange.selectNodeContents(node);
39228         }
39229         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
39230         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
39231
39232         if (nodeIsBefore && !nodeIsAfter)
39233             return 0;
39234         if (!nodeIsBefore && nodeIsAfter)
39235             return 1;
39236         if (nodeIsBefore && nodeIsAfter)
39237             return 2;
39238
39239         return 3;
39240     },
39241
39242     // private? - in a new class?
39243     cleanUpPaste :  function()
39244     {
39245         // cleans up the whole document..
39246       //  console.log('cleanuppaste');
39247         this.cleanUpChildren(this.doc.body);
39248         
39249         
39250     },
39251     cleanUpChildren : function (n)
39252     {
39253         if (!n.childNodes.length) {
39254             return;
39255         }
39256         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39257            this.cleanUpChild(n.childNodes[i]);
39258         }
39259     },
39260     
39261     
39262         
39263     
39264     cleanUpChild : function (node)
39265     {
39266         //console.log(node);
39267         if (node.nodeName == "#text") {
39268             // clean up silly Windows -- stuff?
39269             return; 
39270         }
39271         if (node.nodeName == "#comment") {
39272             node.parentNode.removeChild(node);
39273             // clean up silly Windows -- stuff?
39274             return; 
39275         }
39276         
39277         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39278             // remove node.
39279             node.parentNode.removeChild(node);
39280             return;
39281             
39282         }
39283         if (!node.attributes || !node.attributes.length) {
39284             this.cleanUpChildren(node);
39285             return;
39286         }
39287         
39288         function cleanAttr(n,v)
39289         {
39290             
39291             if (v.match(/^\./) || v.match(/^\//)) {
39292                 return;
39293             }
39294             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39295                 return;
39296             }
39297             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39298             node.removeAttribute(n);
39299             
39300         }
39301         
39302         function cleanStyle(n,v)
39303         {
39304             if (v.match(/expression/)) { //XSS?? should we even bother..
39305                 node.removeAttribute(n);
39306                 return;
39307             }
39308             
39309             
39310             var parts = v.split(/;/);
39311             Roo.each(parts, function(p) {
39312                 p = p.replace(/\s+/g,'');
39313                 if (!p.length) {
39314                     return;
39315                 }
39316                 var l = p.split(':').shift().replace(/\s+/g,'');
39317                 
39318                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39319                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39320                     node.removeAttribute(n);
39321                     return false;
39322                 }
39323             });
39324             
39325             
39326         }
39327         
39328         
39329         for (var i = node.attributes.length-1; i > -1 ; i--) {
39330             var a = node.attributes[i];
39331             //console.log(a);
39332             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39333                 node.removeAttribute(a.name);
39334                 return;
39335             }
39336             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39337                 cleanAttr(a.name,a.value); // fixme..
39338                 return;
39339             }
39340             if (a.name == 'style') {
39341                 cleanStyle(a.name,a.value);
39342             }
39343             /// clean up MS crap..
39344             if (a.name == 'class') {
39345                 if (a.value.match(/^Mso/)) {
39346                     node.className = '';
39347                 }
39348             }
39349             
39350             // style cleanup!?
39351             // class cleanup?
39352             
39353         }
39354         
39355         
39356         this.cleanUpChildren(node);
39357         
39358         
39359     }
39360     
39361     
39362     // hide stuff that is not compatible
39363     /**
39364      * @event blur
39365      * @hide
39366      */
39367     /**
39368      * @event change
39369      * @hide
39370      */
39371     /**
39372      * @event focus
39373      * @hide
39374      */
39375     /**
39376      * @event specialkey
39377      * @hide
39378      */
39379     /**
39380      * @cfg {String} fieldClass @hide
39381      */
39382     /**
39383      * @cfg {String} focusClass @hide
39384      */
39385     /**
39386      * @cfg {String} autoCreate @hide
39387      */
39388     /**
39389      * @cfg {String} inputType @hide
39390      */
39391     /**
39392      * @cfg {String} invalidClass @hide
39393      */
39394     /**
39395      * @cfg {String} invalidText @hide
39396      */
39397     /**
39398      * @cfg {String} msgFx @hide
39399      */
39400     /**
39401      * @cfg {String} validateOnBlur @hide
39402      */
39403 });
39404
39405 Roo.form.HtmlEditor.white = [
39406         'area', 'br', 'img', 'input', 'hr', 'wbr',
39407         
39408        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39409        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39410        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39411        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39412        'table',   'ul',         'xmp', 
39413        
39414        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39415       'thead',   'tr', 
39416      
39417       'dir', 'menu', 'ol', 'ul', 'dl',
39418        
39419       'embed',  'object'
39420 ];
39421
39422
39423 Roo.form.HtmlEditor.black = [
39424     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39425         'applet', // 
39426         'base',   'basefont', 'bgsound', 'blink',  'body', 
39427         'frame',  'frameset', 'head',    'html',   'ilayer', 
39428         'iframe', 'layer',  'link',     'meta',    'object',   
39429         'script', 'style' ,'title',  'xml' // clean later..
39430 ];
39431 Roo.form.HtmlEditor.clean = [
39432     'script', 'style', 'title', 'xml'
39433 ];
39434
39435 // attributes..
39436
39437 Roo.form.HtmlEditor.ablack = [
39438     'on'
39439 ];
39440     
39441 Roo.form.HtmlEditor.aclean = [ 
39442     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39443 ];
39444
39445 // protocols..
39446 Roo.form.HtmlEditor.pwhite= [
39447         'http',  'https',  'mailto'
39448 ];
39449
39450 Roo.form.HtmlEditor.cwhite= [
39451         'text-align',
39452         'font-size'
39453 ];
39454
39455 // <script type="text/javascript">
39456 /*
39457  * Based on
39458  * Ext JS Library 1.1.1
39459  * Copyright(c) 2006-2007, Ext JS, LLC.
39460  *  
39461  
39462  */
39463
39464 /**
39465  * @class Roo.form.HtmlEditorToolbar1
39466  * Basic Toolbar
39467  * 
39468  * Usage:
39469  *
39470  new Roo.form.HtmlEditor({
39471     ....
39472     toolbars : [
39473         new Roo.form.HtmlEditorToolbar1({
39474             disable : { fonts: 1 , format: 1, ..., ... , ...],
39475             btns : [ .... ]
39476         })
39477     }
39478      
39479  * 
39480  * @cfg {Object} disable List of elements to disable..
39481  * @cfg {Array} btns List of additional buttons.
39482  * 
39483  * 
39484  * NEEDS Extra CSS? 
39485  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39486  */
39487  
39488 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39489 {
39490     
39491     Roo.apply(this, config);
39492     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39493     // dont call parent... till later.
39494 }
39495
39496 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39497     
39498     tb: false,
39499     
39500     rendered: false,
39501     
39502     editor : false,
39503     /**
39504      * @cfg {Object} disable  List of toolbar elements to disable
39505          
39506      */
39507     disable : false,
39508       /**
39509      * @cfg {Array} fontFamilies An array of available font families
39510      */
39511     fontFamilies : [
39512         'Arial',
39513         'Courier New',
39514         'Tahoma',
39515         'Times New Roman',
39516         'Verdana'
39517     ],
39518     
39519     specialChars : [
39520            "&#169;",
39521           "&#174;",     
39522           "&#8482;",    
39523           "&#163;" ,    
39524          // "&#8212;",    
39525           "&#8230;",    
39526           "&#247;" ,    
39527         //  "&#225;" ,     ?? a acute?
39528            "&#8364;"    , //Euro
39529        //   "&#8220;"    ,
39530         //  "&#8221;"    ,
39531         //  "&#8226;"    ,
39532           "&#176;"  //   , // degrees
39533
39534          // "&#233;"     , // e ecute
39535          // "&#250;"     , // u ecute?
39536     ],
39537     inputElements : [ 
39538             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39539             "input:submit", "input:button", "select", "textarea", "label" ],
39540     formats : [
39541         ["p"] ,  
39542         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39543         ["pre"],[ "code"], 
39544         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39545     ],
39546      /**
39547      * @cfg {String} defaultFont default font to use.
39548      */
39549     defaultFont: 'tahoma',
39550    
39551     fontSelect : false,
39552     
39553     
39554     formatCombo : false,
39555     
39556     init : function(editor)
39557     {
39558         this.editor = editor;
39559         
39560         
39561         var fid = editor.frameId;
39562         var etb = this;
39563         function btn(id, toggle, handler){
39564             var xid = fid + '-'+ id ;
39565             return {
39566                 id : xid,
39567                 cmd : id,
39568                 cls : 'x-btn-icon x-edit-'+id,
39569                 enableToggle:toggle !== false,
39570                 scope: editor, // was editor...
39571                 handler:handler||editor.relayBtnCmd,
39572                 clickEvent:'mousedown',
39573                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39574                 tabIndex:-1
39575             };
39576         }
39577         
39578         
39579         
39580         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39581         this.tb = tb;
39582          // stop form submits
39583         tb.el.on('click', function(e){
39584             e.preventDefault(); // what does this do?
39585         });
39586
39587         if(!this.disable.font && !Roo.isSafari){
39588             /* why no safari for fonts
39589             editor.fontSelect = tb.el.createChild({
39590                 tag:'select',
39591                 tabIndex: -1,
39592                 cls:'x-font-select',
39593                 html: editor.createFontOptions()
39594             });
39595             editor.fontSelect.on('change', function(){
39596                 var font = editor.fontSelect.dom.value;
39597                 editor.relayCmd('fontname', font);
39598                 editor.deferFocus();
39599             }, editor);
39600             tb.add(
39601                 editor.fontSelect.dom,
39602                 '-'
39603             );
39604             */
39605         };
39606         if(!this.disable.formats){
39607             this.formatCombo = new Roo.form.ComboBox({
39608                 store: new Roo.data.SimpleStore({
39609                     id : 'tag',
39610                     fields: ['tag'],
39611                     data : this.formats // from states.js
39612                 }),
39613                 blockFocus : true,
39614                 //autoCreate : {tag: "div",  size: "20"},
39615                 displayField:'tag',
39616                 typeAhead: false,
39617                 mode: 'local',
39618                 editable : false,
39619                 triggerAction: 'all',
39620                 emptyText:'Add tag',
39621                 selectOnFocus:true,
39622                 width:135,
39623                 listeners : {
39624                     'select': function(c, r, i) {
39625                         editor.insertTag(r.get('tag'));
39626                         editor.focus();
39627                     }
39628                 }
39629
39630             });
39631             tb.addField(this.formatCombo);
39632             
39633         }
39634         
39635         if(!this.disable.format){
39636             tb.add(
39637                 btn('bold'),
39638                 btn('italic'),
39639                 btn('underline')
39640             );
39641         };
39642         if(!this.disable.fontSize){
39643             tb.add(
39644                 '-',
39645                 
39646                 
39647                 btn('increasefontsize', false, editor.adjustFont),
39648                 btn('decreasefontsize', false, editor.adjustFont)
39649             );
39650         };
39651         
39652         
39653         if(this.disable.colors){
39654             tb.add(
39655                 '-', {
39656                     id:editor.frameId +'-forecolor',
39657                     cls:'x-btn-icon x-edit-forecolor',
39658                     clickEvent:'mousedown',
39659                     tooltip: this.buttonTips['forecolor'] || undefined,
39660                     tabIndex:-1,
39661                     menu : new Roo.menu.ColorMenu({
39662                         allowReselect: true,
39663                         focus: Roo.emptyFn,
39664                         value:'000000',
39665                         plain:true,
39666                         selectHandler: function(cp, color){
39667                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
39668                             editor.deferFocus();
39669                         },
39670                         scope: editor,
39671                         clickEvent:'mousedown'
39672                     })
39673                 }, {
39674                     id:editor.frameId +'backcolor',
39675                     cls:'x-btn-icon x-edit-backcolor',
39676                     clickEvent:'mousedown',
39677                     tooltip: this.buttonTips['backcolor'] || undefined,
39678                     tabIndex:-1,
39679                     menu : new Roo.menu.ColorMenu({
39680                         focus: Roo.emptyFn,
39681                         value:'FFFFFF',
39682                         plain:true,
39683                         allowReselect: true,
39684                         selectHandler: function(cp, color){
39685                             if(Roo.isGecko){
39686                                 editor.execCmd('useCSS', false);
39687                                 editor.execCmd('hilitecolor', color);
39688                                 editor.execCmd('useCSS', true);
39689                                 editor.deferFocus();
39690                             }else{
39691                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
39692                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
39693                                 editor.deferFocus();
39694                             }
39695                         },
39696                         scope:editor,
39697                         clickEvent:'mousedown'
39698                     })
39699                 }
39700             );
39701         };
39702         // now add all the items...
39703         
39704
39705         if(!this.disable.alignments){
39706             tb.add(
39707                 '-',
39708                 btn('justifyleft'),
39709                 btn('justifycenter'),
39710                 btn('justifyright')
39711             );
39712         };
39713
39714         //if(!Roo.isSafari){
39715             if(!this.disable.links){
39716                 tb.add(
39717                     '-',
39718                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
39719                 );
39720             };
39721
39722             if(!this.disable.lists){
39723                 tb.add(
39724                     '-',
39725                     btn('insertorderedlist'),
39726                     btn('insertunorderedlist')
39727                 );
39728             }
39729             if(!this.disable.sourceEdit){
39730                 tb.add(
39731                     '-',
39732                     btn('sourceedit', true, function(btn){
39733                         this.toggleSourceEdit(btn.pressed);
39734                     })
39735                 );
39736             }
39737         //}
39738         
39739         var smenu = { };
39740         // special menu.. - needs to be tidied up..
39741         if (!this.disable.special) {
39742             smenu = {
39743                 text: "&#169;",
39744                 cls: 'x-edit-none',
39745                 menu : {
39746                     items : []
39747                    }
39748             };
39749             for (var i =0; i < this.specialChars.length; i++) {
39750                 smenu.menu.items.push({
39751                     
39752                     html: this.specialChars[i],
39753                     handler: function(a,b) {
39754                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
39755                         
39756                     },
39757                     tabIndex:-1
39758                 });
39759             }
39760             
39761             
39762             tb.add(smenu);
39763             
39764             
39765         }
39766         if (this.btns) {
39767             for(var i =0; i< this.btns.length;i++) {
39768                 var b = this.btns[i];
39769                 b.cls =  'x-edit-none';
39770                 b.scope = editor;
39771                 tb.add(b);
39772             }
39773         
39774         }
39775         
39776         
39777         
39778         // disable everything...
39779         
39780         this.tb.items.each(function(item){
39781            if(item.id != editor.frameId+ '-sourceedit'){
39782                 item.disable();
39783             }
39784         });
39785         this.rendered = true;
39786         
39787         // the all the btns;
39788         editor.on('editorevent', this.updateToolbar, this);
39789         // other toolbars need to implement this..
39790         //editor.on('editmodechange', this.updateToolbar, this);
39791     },
39792     
39793     
39794     
39795     /**
39796      * Protected method that will not generally be called directly. It triggers
39797      * a toolbar update by reading the markup state of the current selection in the editor.
39798      */
39799     updateToolbar: function(){
39800
39801         if(!this.editor.activated){
39802             this.editor.onFirstFocus();
39803             return;
39804         }
39805
39806         var btns = this.tb.items.map, 
39807             doc = this.editor.doc,
39808             frameId = this.editor.frameId;
39809
39810         if(!this.disable.font && !Roo.isSafari){
39811             /*
39812             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
39813             if(name != this.fontSelect.dom.value){
39814                 this.fontSelect.dom.value = name;
39815             }
39816             */
39817         }
39818         if(!this.disable.format){
39819             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
39820             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
39821             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
39822         }
39823         if(!this.disable.alignments){
39824             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
39825             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
39826             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
39827         }
39828         if(!Roo.isSafari && !this.disable.lists){
39829             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
39830             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
39831         }
39832         
39833         var ans = this.editor.getAllAncestors();
39834         if (this.formatCombo) {
39835             
39836             
39837             var store = this.formatCombo.store;
39838             this.formatCombo.setValue("");
39839             for (var i =0; i < ans.length;i++) {
39840                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
39841                     // select it..
39842                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
39843                     break;
39844                 }
39845             }
39846         }
39847         
39848         
39849         
39850         // hides menus... - so this cant be on a menu...
39851         Roo.menu.MenuMgr.hideAll();
39852
39853         //this.editorsyncValue();
39854     },
39855    
39856     
39857     createFontOptions : function(){
39858         var buf = [], fs = this.fontFamilies, ff, lc;
39859         for(var i = 0, len = fs.length; i< len; i++){
39860             ff = fs[i];
39861             lc = ff.toLowerCase();
39862             buf.push(
39863                 '<option value="',lc,'" style="font-family:',ff,';"',
39864                     (this.defaultFont == lc ? ' selected="true">' : '>'),
39865                     ff,
39866                 '</option>'
39867             );
39868         }
39869         return buf.join('');
39870     },
39871     
39872     toggleSourceEdit : function(sourceEditMode){
39873         if(sourceEditMode === undefined){
39874             sourceEditMode = !this.sourceEditMode;
39875         }
39876         this.sourceEditMode = sourceEditMode === true;
39877         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
39878         // just toggle the button?
39879         if(btn.pressed !== this.editor.sourceEditMode){
39880             btn.toggle(this.editor.sourceEditMode);
39881             return;
39882         }
39883         
39884         if(this.sourceEditMode){
39885             this.tb.items.each(function(item){
39886                 if(item.cmd != 'sourceedit'){
39887                     item.disable();
39888                 }
39889             });
39890           
39891         }else{
39892             if(this.initialized){
39893                 this.tb.items.each(function(item){
39894                     item.enable();
39895                 });
39896             }
39897             
39898         }
39899         // tell the editor that it's been pressed..
39900         this.editor.toggleSourceEdit(sourceEditMode);
39901        
39902     },
39903      /**
39904      * Object collection of toolbar tooltips for the buttons in the editor. The key
39905      * is the command id associated with that button and the value is a valid QuickTips object.
39906      * For example:
39907 <pre><code>
39908 {
39909     bold : {
39910         title: 'Bold (Ctrl+B)',
39911         text: 'Make the selected text bold.',
39912         cls: 'x-html-editor-tip'
39913     },
39914     italic : {
39915         title: 'Italic (Ctrl+I)',
39916         text: 'Make the selected text italic.',
39917         cls: 'x-html-editor-tip'
39918     },
39919     ...
39920 </code></pre>
39921     * @type Object
39922      */
39923     buttonTips : {
39924         bold : {
39925             title: 'Bold (Ctrl+B)',
39926             text: 'Make the selected text bold.',
39927             cls: 'x-html-editor-tip'
39928         },
39929         italic : {
39930             title: 'Italic (Ctrl+I)',
39931             text: 'Make the selected text italic.',
39932             cls: 'x-html-editor-tip'
39933         },
39934         underline : {
39935             title: 'Underline (Ctrl+U)',
39936             text: 'Underline the selected text.',
39937             cls: 'x-html-editor-tip'
39938         },
39939         increasefontsize : {
39940             title: 'Grow Text',
39941             text: 'Increase the font size.',
39942             cls: 'x-html-editor-tip'
39943         },
39944         decreasefontsize : {
39945             title: 'Shrink Text',
39946             text: 'Decrease the font size.',
39947             cls: 'x-html-editor-tip'
39948         },
39949         backcolor : {
39950             title: 'Text Highlight Color',
39951             text: 'Change the background color of the selected text.',
39952             cls: 'x-html-editor-tip'
39953         },
39954         forecolor : {
39955             title: 'Font Color',
39956             text: 'Change the color of the selected text.',
39957             cls: 'x-html-editor-tip'
39958         },
39959         justifyleft : {
39960             title: 'Align Text Left',
39961             text: 'Align text to the left.',
39962             cls: 'x-html-editor-tip'
39963         },
39964         justifycenter : {
39965             title: 'Center Text',
39966             text: 'Center text in the editor.',
39967             cls: 'x-html-editor-tip'
39968         },
39969         justifyright : {
39970             title: 'Align Text Right',
39971             text: 'Align text to the right.',
39972             cls: 'x-html-editor-tip'
39973         },
39974         insertunorderedlist : {
39975             title: 'Bullet List',
39976             text: 'Start a bulleted list.',
39977             cls: 'x-html-editor-tip'
39978         },
39979         insertorderedlist : {
39980             title: 'Numbered List',
39981             text: 'Start a numbered list.',
39982             cls: 'x-html-editor-tip'
39983         },
39984         createlink : {
39985             title: 'Hyperlink',
39986             text: 'Make the selected text a hyperlink.',
39987             cls: 'x-html-editor-tip'
39988         },
39989         sourceedit : {
39990             title: 'Source Edit',
39991             text: 'Switch to source editing mode.',
39992             cls: 'x-html-editor-tip'
39993         }
39994     },
39995     // private
39996     onDestroy : function(){
39997         if(this.rendered){
39998             
39999             this.tb.items.each(function(item){
40000                 if(item.menu){
40001                     item.menu.removeAll();
40002                     if(item.menu.el){
40003                         item.menu.el.destroy();
40004                     }
40005                 }
40006                 item.destroy();
40007             });
40008              
40009         }
40010     },
40011     onFirstFocus: function() {
40012         this.tb.items.each(function(item){
40013            item.enable();
40014         });
40015     }
40016 });
40017
40018
40019
40020
40021 // <script type="text/javascript">
40022 /*
40023  * Based on
40024  * Ext JS Library 1.1.1
40025  * Copyright(c) 2006-2007, Ext JS, LLC.
40026  *  
40027  
40028  */
40029
40030  
40031 /**
40032  * @class Roo.form.HtmlEditor.ToolbarContext
40033  * Context Toolbar
40034  * 
40035  * Usage:
40036  *
40037  new Roo.form.HtmlEditor({
40038     ....
40039     toolbars : [
40040         new Roo.form.HtmlEditor.ToolbarStandard(),
40041         new Roo.form.HtmlEditor.ToolbarContext()
40042         })
40043     }
40044      
40045  * 
40046  * @config : {Object} disable List of elements to disable.. (not done yet.)
40047  * 
40048  * 
40049  */
40050
40051 Roo.form.HtmlEditor.ToolbarContext = function(config)
40052 {
40053     
40054     Roo.apply(this, config);
40055     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40056     // dont call parent... till later.
40057 }
40058 Roo.form.HtmlEditor.ToolbarContext.types = {
40059     'IMG' : {
40060         width : {
40061             title: "Width",
40062             width: 40
40063         },
40064         height:  {
40065             title: "Height",
40066             width: 40
40067         },
40068         align: {
40069             title: "Align",
40070             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40071             width : 80
40072             
40073         },
40074         border: {
40075             title: "Border",
40076             width: 40
40077         },
40078         alt: {
40079             title: "Alt",
40080             width: 120
40081         },
40082         src : {
40083             title: "Src",
40084             width: 220
40085         }
40086         
40087     },
40088     'A' : {
40089         name : {
40090             title: "Name",
40091             width: 50
40092         },
40093         href:  {
40094             title: "Href",
40095             width: 220
40096         } // border?
40097         
40098     },
40099     'TABLE' : {
40100         rows : {
40101             title: "Rows",
40102             width: 20
40103         },
40104         cols : {
40105             title: "Cols",
40106             width: 20
40107         },
40108         width : {
40109             title: "Width",
40110             width: 40
40111         },
40112         height : {
40113             title: "Height",
40114             width: 40
40115         },
40116         border : {
40117             title: "Border",
40118             width: 20
40119         }
40120     },
40121     'TD' : {
40122         width : {
40123             title: "Width",
40124             width: 40
40125         },
40126         height : {
40127             title: "Height",
40128             width: 40
40129         },   
40130         align: {
40131             title: "Align",
40132             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40133             width: 40
40134         },
40135         valign: {
40136             title: "Valign",
40137             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40138             width: 40
40139         },
40140         colspan: {
40141             title: "Colspan",
40142             width: 20
40143             
40144         }
40145     },
40146     'INPUT' : {
40147         name : {
40148             title: "name",
40149             width: 120
40150         },
40151         value : {
40152             title: "Value",
40153             width: 120
40154         },
40155         width : {
40156             title: "Width",
40157             width: 40
40158         }
40159     },
40160     'LABEL' : {
40161         'for' : {
40162             title: "For",
40163             width: 120
40164         }
40165     },
40166     'TEXTAREA' : {
40167           name : {
40168             title: "name",
40169             width: 120
40170         },
40171         rows : {
40172             title: "Rows",
40173             width: 20
40174         },
40175         cols : {
40176             title: "Cols",
40177             width: 20
40178         }
40179     },
40180     'SELECT' : {
40181         name : {
40182             title: "name",
40183             width: 120
40184         },
40185         selectoptions : {
40186             title: "Options",
40187             width: 200
40188         }
40189     },
40190     'BODY' : {
40191         title : {
40192             title: "title",
40193             width: 120,
40194             disabled : true
40195         }
40196     }
40197 };
40198
40199
40200
40201 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40202     
40203     tb: false,
40204     
40205     rendered: false,
40206     
40207     editor : false,
40208     /**
40209      * @cfg {Object} disable  List of toolbar elements to disable
40210          
40211      */
40212     disable : false,
40213     
40214     
40215     
40216     toolbars : false,
40217     
40218     init : function(editor)
40219     {
40220         this.editor = editor;
40221         
40222         
40223         var fid = editor.frameId;
40224         var etb = this;
40225         function btn(id, toggle, handler){
40226             var xid = fid + '-'+ id ;
40227             return {
40228                 id : xid,
40229                 cmd : id,
40230                 cls : 'x-btn-icon x-edit-'+id,
40231                 enableToggle:toggle !== false,
40232                 scope: editor, // was editor...
40233                 handler:handler||editor.relayBtnCmd,
40234                 clickEvent:'mousedown',
40235                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40236                 tabIndex:-1
40237             };
40238         }
40239         // create a new element.
40240         var wdiv = editor.wrap.createChild({
40241                 tag: 'div'
40242             }, editor.wrap.dom.firstChild.nextSibling, true);
40243         
40244         // can we do this more than once??
40245         
40246          // stop form submits
40247       
40248  
40249         // disable everything...
40250         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40251         this.toolbars = {};
40252            
40253         for (var i in  ty) {
40254           
40255             this.toolbars[i] = this.buildToolbar(ty[i],i);
40256         }
40257         this.tb = this.toolbars.BODY;
40258         this.tb.el.show();
40259         
40260          
40261         this.rendered = true;
40262         
40263         // the all the btns;
40264         editor.on('editorevent', this.updateToolbar, this);
40265         // other toolbars need to implement this..
40266         //editor.on('editmodechange', this.updateToolbar, this);
40267     },
40268     
40269     
40270     
40271     /**
40272      * Protected method that will not generally be called directly. It triggers
40273      * a toolbar update by reading the markup state of the current selection in the editor.
40274      */
40275     updateToolbar: function(){
40276
40277         if(!this.editor.activated){
40278             this.editor.onFirstFocus();
40279             return;
40280         }
40281
40282         
40283         var ans = this.editor.getAllAncestors();
40284         
40285         // pick
40286         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40287         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40288         sel = sel ? sel : this.editor.doc.body;
40289         sel = sel.tagName.length ? sel : this.editor.doc.body;
40290         var tn = sel.tagName.toUpperCase();
40291         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40292         tn = sel.tagName.toUpperCase();
40293         if (this.tb.name  == tn) {
40294             return; // no change
40295         }
40296         this.tb.el.hide();
40297         ///console.log("show: " + tn);
40298         this.tb =  this.toolbars[tn];
40299         this.tb.el.show();
40300         this.tb.fields.each(function(e) {
40301             e.setValue(sel.getAttribute(e.name));
40302         });
40303         this.tb.selectedNode = sel;
40304         
40305         
40306         Roo.menu.MenuMgr.hideAll();
40307
40308         //this.editorsyncValue();
40309     },
40310    
40311        
40312     // private
40313     onDestroy : function(){
40314         if(this.rendered){
40315             
40316             this.tb.items.each(function(item){
40317                 if(item.menu){
40318                     item.menu.removeAll();
40319                     if(item.menu.el){
40320                         item.menu.el.destroy();
40321                     }
40322                 }
40323                 item.destroy();
40324             });
40325              
40326         }
40327     },
40328     onFirstFocus: function() {
40329         // need to do this for all the toolbars..
40330         this.tb.items.each(function(item){
40331            item.enable();
40332         });
40333     },
40334     buildToolbar: function(tlist, nm)
40335     {
40336         var editor = this.editor;
40337          // create a new element.
40338         var wdiv = editor.wrap.createChild({
40339                 tag: 'div'
40340             }, editor.wrap.dom.firstChild.nextSibling, true);
40341         
40342        
40343         var tb = new Roo.Toolbar(wdiv);
40344         tb.add(nm+ ":&nbsp;");
40345         for (var i in tlist) {
40346             var item = tlist[i];
40347             tb.add(item.title + ":&nbsp;");
40348             if (item.opts) {
40349                 // fixme
40350                 
40351               
40352                 tb.addField( new Roo.form.ComboBox({
40353                     store: new Roo.data.SimpleStore({
40354                         id : 'val',
40355                         fields: ['val'],
40356                         data : item.opts // from states.js
40357                     }),
40358                     name : i,
40359                     displayField:'val',
40360                     typeAhead: false,
40361                     mode: 'local',
40362                     editable : false,
40363                     triggerAction: 'all',
40364                     emptyText:'Select',
40365                     selectOnFocus:true,
40366                     width: item.width ? item.width  : 130,
40367                     listeners : {
40368                         'select': function(c, r, i) {
40369                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40370                         }
40371                     }
40372
40373                 }));
40374                 continue;
40375                     
40376                 
40377                 
40378                 
40379                 
40380                 tb.addField( new Roo.form.TextField({
40381                     name: i,
40382                     width: 100,
40383                     //allowBlank:false,
40384                     value: ''
40385                 }));
40386                 continue;
40387             }
40388             tb.addField( new Roo.form.TextField({
40389                 name: i,
40390                 width: item.width,
40391                 //allowBlank:true,
40392                 value: '',
40393                 listeners: {
40394                     'change' : function(f, nv, ov) {
40395                         tb.selectedNode.setAttribute(f.name, nv);
40396                     }
40397                 }
40398             }));
40399              
40400         }
40401         tb.el.on('click', function(e){
40402             e.preventDefault(); // what does this do?
40403         });
40404         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40405         tb.el.hide();
40406         tb.name = nm;
40407         // dont need to disable them... as they will get hidden
40408         return tb;
40409          
40410         
40411     }
40412     
40413     
40414     
40415     
40416 });
40417
40418
40419
40420
40421
40422 /*
40423  * Based on:
40424  * Ext JS Library 1.1.1
40425  * Copyright(c) 2006-2007, Ext JS, LLC.
40426  *
40427  * Originally Released Under LGPL - original licence link has changed is not relivant.
40428  *
40429  * Fork - LGPL
40430  * <script type="text/javascript">
40431  */
40432  
40433 /**
40434  * @class Roo.form.BasicForm
40435  * @extends Roo.util.Observable
40436  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
40437  * @constructor
40438  * @param {String/HTMLElement/Roo.Element} el The form element or its id
40439  * @param {Object} config Configuration options
40440  */
40441 Roo.form.BasicForm = function(el, config){
40442     this.allItems = [];
40443     this.childForms = [];
40444     Roo.apply(this, config);
40445     /*
40446      * The Roo.form.Field items in this form.
40447      * @type MixedCollection
40448      */
40449      
40450      
40451     this.items = new Roo.util.MixedCollection(false, function(o){
40452         return o.id || (o.id = Roo.id());
40453     });
40454     this.addEvents({
40455         /**
40456          * @event beforeaction
40457          * Fires before any action is performed. Return false to cancel the action.
40458          * @param {Form} this
40459          * @param {Action} action The action to be performed
40460          */
40461         beforeaction: true,
40462         /**
40463          * @event actionfailed
40464          * Fires when an action fails.
40465          * @param {Form} this
40466          * @param {Action} action The action that failed
40467          */
40468         actionfailed : true,
40469         /**
40470          * @event actioncomplete
40471          * Fires when an action is completed.
40472          * @param {Form} this
40473          * @param {Action} action The action that completed
40474          */
40475         actioncomplete : true
40476     });
40477     if(el){
40478         this.initEl(el);
40479     }
40480     Roo.form.BasicForm.superclass.constructor.call(this);
40481 };
40482
40483 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
40484     /**
40485      * @cfg {String} method
40486      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
40487      */
40488     /**
40489      * @cfg {DataReader} reader
40490      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
40491      * This is optional as there is built-in support for processing JSON.
40492      */
40493     /**
40494      * @cfg {DataReader} errorReader
40495      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
40496      * This is completely optional as there is built-in support for processing JSON.
40497      */
40498     /**
40499      * @cfg {String} url
40500      * The URL to use for form actions if one isn't supplied in the action options.
40501      */
40502     /**
40503      * @cfg {Boolean} fileUpload
40504      * Set to true if this form is a file upload.
40505      */
40506      
40507     /**
40508      * @cfg {Object} baseParams
40509      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
40510      */
40511      /**
40512      
40513     /**
40514      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
40515      */
40516     timeout: 30,
40517
40518     // private
40519     activeAction : null,
40520
40521     /**
40522      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
40523      * or setValues() data instead of when the form was first created.
40524      */
40525     trackResetOnLoad : false,
40526     
40527     
40528     /**
40529      * childForms - used for multi-tab forms
40530      * @type {Array}
40531      */
40532     childForms : false,
40533     
40534     /**
40535      * allItems - full list of fields.
40536      * @type {Array}
40537      */
40538     allItems : false,
40539     
40540     /**
40541      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
40542      * element by passing it or its id or mask the form itself by passing in true.
40543      * @type Mixed
40544      */
40545     waitMsgTarget : false,
40546
40547     // private
40548     initEl : function(el){
40549         this.el = Roo.get(el);
40550         this.id = this.el.id || Roo.id();
40551         this.el.on('submit', this.onSubmit, this);
40552         this.el.addClass('x-form');
40553     },
40554
40555     // private
40556     onSubmit : function(e){
40557         e.stopEvent();
40558     },
40559
40560     /**
40561      * Returns true if client-side validation on the form is successful.
40562      * @return Boolean
40563      */
40564     isValid : function(){
40565         var valid = true;
40566         this.items.each(function(f){
40567            if(!f.validate()){
40568                valid = false;
40569            }
40570         });
40571         return valid;
40572     },
40573
40574     /**
40575      * Returns true if any fields in this form have changed since their original load.
40576      * @return Boolean
40577      */
40578     isDirty : function(){
40579         var dirty = false;
40580         this.items.each(function(f){
40581            if(f.isDirty()){
40582                dirty = true;
40583                return false;
40584            }
40585         });
40586         return dirty;
40587     },
40588
40589     /**
40590      * Performs a predefined action (submit or load) or custom actions you define on this form.
40591      * @param {String} actionName The name of the action type
40592      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
40593      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
40594      * accept other config options):
40595      * <pre>
40596 Property          Type             Description
40597 ----------------  ---------------  ----------------------------------------------------------------------------------
40598 url               String           The url for the action (defaults to the form's url)
40599 method            String           The form method to use (defaults to the form's method, or POST if not defined)
40600 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
40601 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
40602                                    validate the form on the client (defaults to false)
40603      * </pre>
40604      * @return {BasicForm} this
40605      */
40606     doAction : function(action, options){
40607         if(typeof action == 'string'){
40608             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
40609         }
40610         if(this.fireEvent('beforeaction', this, action) !== false){
40611             this.beforeAction(action);
40612             action.run.defer(100, action);
40613         }
40614         return this;
40615     },
40616
40617     /**
40618      * Shortcut to do a submit action.
40619      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40620      * @return {BasicForm} this
40621      */
40622     submit : function(options){
40623         this.doAction('submit', options);
40624         return this;
40625     },
40626
40627     /**
40628      * Shortcut to do a load action.
40629      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40630      * @return {BasicForm} this
40631      */
40632     load : function(options){
40633         this.doAction('load', options);
40634         return this;
40635     },
40636
40637     /**
40638      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
40639      * @param {Record} record The record to edit
40640      * @return {BasicForm} this
40641      */
40642     updateRecord : function(record){
40643         record.beginEdit();
40644         var fs = record.fields;
40645         fs.each(function(f){
40646             var field = this.findField(f.name);
40647             if(field){
40648                 record.set(f.name, field.getValue());
40649             }
40650         }, this);
40651         record.endEdit();
40652         return this;
40653     },
40654
40655     /**
40656      * Loads an Roo.data.Record into this form.
40657      * @param {Record} record The record to load
40658      * @return {BasicForm} this
40659      */
40660     loadRecord : function(record){
40661         this.setValues(record.data);
40662         return this;
40663     },
40664
40665     // private
40666     beforeAction : function(action){
40667         var o = action.options;
40668         
40669        
40670         if(this.waitMsgTarget === true){
40671             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
40672         }else if(this.waitMsgTarget){
40673             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
40674             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
40675         }else {
40676             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
40677         }
40678          
40679     },
40680
40681     // private
40682     afterAction : function(action, success){
40683         this.activeAction = null;
40684         var o = action.options;
40685         
40686         if(this.waitMsgTarget === true){
40687             this.el.unmask();
40688         }else if(this.waitMsgTarget){
40689             this.waitMsgTarget.unmask();
40690         }else{
40691             Roo.MessageBox.updateProgress(1);
40692             Roo.MessageBox.hide();
40693         }
40694          
40695         if(success){
40696             if(o.reset){
40697                 this.reset();
40698             }
40699             Roo.callback(o.success, o.scope, [this, action]);
40700             this.fireEvent('actioncomplete', this, action);
40701             
40702         }else{
40703             Roo.callback(o.failure, o.scope, [this, action]);
40704             // show an error message if no failed handler is set..
40705             if (!this.hasListener('actionfailed')) {
40706                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
40707             }
40708             
40709             this.fireEvent('actionfailed', this, action);
40710         }
40711         
40712     },
40713
40714     /**
40715      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
40716      * @param {String} id The value to search for
40717      * @return Field
40718      */
40719     findField : function(id){
40720         var field = this.items.get(id);
40721         if(!field){
40722             this.items.each(function(f){
40723                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
40724                     field = f;
40725                     return false;
40726                 }
40727             });
40728         }
40729         return field || null;
40730     },
40731
40732     /**
40733      * Add a secondary form to this one, 
40734      * Used to provide tabbed forms. One form is primary, with hidden values 
40735      * which mirror the elements from the other forms.
40736      * 
40737      * @param {Roo.form.Form} form to add.
40738      * 
40739      */
40740     addForm : function(form)
40741     {
40742        
40743         if (this.childForms.indexOf(form) > -1) {
40744             // already added..
40745             return;
40746         }
40747         this.childForms.push(form);
40748         var n = '';
40749         Roo.each(form.allItems, function (fe) {
40750             
40751             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
40752             if (this.findField(n)) { // already added..
40753                 return;
40754             }
40755             var add = new Roo.form.Hidden({
40756                 name : n
40757             });
40758             add.render(this.el);
40759             
40760             this.add( add );
40761         }, this);
40762         
40763     },
40764     /**
40765      * Mark fields in this form invalid in bulk.
40766      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
40767      * @return {BasicForm} this
40768      */
40769     markInvalid : function(errors){
40770         if(errors instanceof Array){
40771             for(var i = 0, len = errors.length; i < len; i++){
40772                 var fieldError = errors[i];
40773                 var f = this.findField(fieldError.id);
40774                 if(f){
40775                     f.markInvalid(fieldError.msg);
40776                 }
40777             }
40778         }else{
40779             var field, id;
40780             for(id in errors){
40781                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
40782                     field.markInvalid(errors[id]);
40783                 }
40784             }
40785         }
40786         Roo.each(this.childForms || [], function (f) {
40787             f.markInvalid(errors);
40788         });
40789         
40790         return this;
40791     },
40792
40793     /**
40794      * Set values for fields in this form in bulk.
40795      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
40796      * @return {BasicForm} this
40797      */
40798     setValues : function(values){
40799         if(values instanceof Array){ // array of objects
40800             for(var i = 0, len = values.length; i < len; i++){
40801                 var v = values[i];
40802                 var f = this.findField(v.id);
40803                 if(f){
40804                     f.setValue(v.value);
40805                     if(this.trackResetOnLoad){
40806                         f.originalValue = f.getValue();
40807                     }
40808                 }
40809             }
40810         }else{ // object hash
40811             var field, id;
40812             for(id in values){
40813                 if(typeof values[id] != 'function' && (field = this.findField(id))){
40814                     
40815                     if (field.setFromData && 
40816                         field.valueField && 
40817                         field.displayField &&
40818                         // combos' with local stores can 
40819                         // be queried via setValue()
40820                         // to set their value..
40821                         (field.store && !field.store.isLocal)
40822                         ) {
40823                         // it's a combo
40824                         var sd = { };
40825                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
40826                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
40827                         field.setFromData(sd);
40828                         
40829                     } else {
40830                         field.setValue(values[id]);
40831                     }
40832                     
40833                     
40834                     if(this.trackResetOnLoad){
40835                         field.originalValue = field.getValue();
40836                     }
40837                 }
40838             }
40839         }
40840          
40841         Roo.each(this.childForms || [], function (f) {
40842             f.setValues(values);
40843         });
40844                 
40845         return this;
40846     },
40847
40848     /**
40849      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
40850      * they are returned as an array.
40851      * @param {Boolean} asString
40852      * @return {Object}
40853      */
40854     getValues : function(asString){
40855         if (this.childForms) {
40856             // copy values from the child forms
40857             Roo.each(this.childForms, function (f) {
40858                 this.setValues(f.getValues());
40859             }, this);
40860         }
40861         
40862         
40863         
40864         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
40865         if(asString === true){
40866             return fs;
40867         }
40868         return Roo.urlDecode(fs);
40869     },
40870     
40871     /**
40872      * Returns the fields in this form as an object with key/value pairs. 
40873      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
40874      * @return {Object}
40875      */
40876     getFieldValues : function()
40877     {
40878         if (this.childForms) {
40879             // copy values from the child forms
40880             Roo.each(this.childForms, function (f) {
40881                 this.setValues(f.getValues());
40882             }, this);
40883         }
40884         
40885         var ret = {};
40886         this.items.each(function(f){
40887             if (!f.getName()) {
40888                 return;
40889             }
40890             var v = f.getValue();
40891             if ((typeof(v) == 'object') && f.getRawValue) {
40892                 v = f.getRawValue() ; // dates..
40893             }
40894             ret[f.getName()] = v;
40895         });
40896         
40897         return ret;
40898     },
40899
40900     /**
40901      * Clears all invalid messages in this form.
40902      * @return {BasicForm} this
40903      */
40904     clearInvalid : function(){
40905         this.items.each(function(f){
40906            f.clearInvalid();
40907         });
40908         
40909         Roo.each(this.childForms || [], function (f) {
40910             f.clearInvalid();
40911         });
40912         
40913         
40914         return this;
40915     },
40916
40917     /**
40918      * Resets this form.
40919      * @return {BasicForm} this
40920      */
40921     reset : function(){
40922         this.items.each(function(f){
40923             f.reset();
40924         });
40925         
40926         Roo.each(this.childForms || [], function (f) {
40927             f.reset();
40928         });
40929        
40930         
40931         return this;
40932     },
40933
40934     /**
40935      * Add Roo.form components to this form.
40936      * @param {Field} field1
40937      * @param {Field} field2 (optional)
40938      * @param {Field} etc (optional)
40939      * @return {BasicForm} this
40940      */
40941     add : function(){
40942         this.items.addAll(Array.prototype.slice.call(arguments, 0));
40943         return this;
40944     },
40945
40946
40947     /**
40948      * Removes a field from the items collection (does NOT remove its markup).
40949      * @param {Field} field
40950      * @return {BasicForm} this
40951      */
40952     remove : function(field){
40953         this.items.remove(field);
40954         return this;
40955     },
40956
40957     /**
40958      * Looks at the fields in this form, checks them for an id attribute,
40959      * and calls applyTo on the existing dom element with that id.
40960      * @return {BasicForm} this
40961      */
40962     render : function(){
40963         this.items.each(function(f){
40964             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
40965                 f.applyTo(f.id);
40966             }
40967         });
40968         return this;
40969     },
40970
40971     /**
40972      * Calls {@link Ext#apply} for all fields in this form with the passed object.
40973      * @param {Object} values
40974      * @return {BasicForm} this
40975      */
40976     applyToFields : function(o){
40977         this.items.each(function(f){
40978            Roo.apply(f, o);
40979         });
40980         return this;
40981     },
40982
40983     /**
40984      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
40985      * @param {Object} values
40986      * @return {BasicForm} this
40987      */
40988     applyIfToFields : function(o){
40989         this.items.each(function(f){
40990            Roo.applyIf(f, o);
40991         });
40992         return this;
40993     }
40994 });
40995
40996 // back compat
40997 Roo.BasicForm = Roo.form.BasicForm;/*
40998  * Based on:
40999  * Ext JS Library 1.1.1
41000  * Copyright(c) 2006-2007, Ext JS, LLC.
41001  *
41002  * Originally Released Under LGPL - original licence link has changed is not relivant.
41003  *
41004  * Fork - LGPL
41005  * <script type="text/javascript">
41006  */
41007
41008 /**
41009  * @class Roo.form.Form
41010  * @extends Roo.form.BasicForm
41011  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41012  * @constructor
41013  * @param {Object} config Configuration options
41014  */
41015 Roo.form.Form = function(config){
41016     var xitems =  [];
41017     if (config.items) {
41018         xitems = config.items;
41019         delete config.items;
41020     }
41021    
41022     
41023     Roo.form.Form.superclass.constructor.call(this, null, config);
41024     this.url = this.url || this.action;
41025     if(!this.root){
41026         this.root = new Roo.form.Layout(Roo.applyIf({
41027             id: Roo.id()
41028         }, config));
41029     }
41030     this.active = this.root;
41031     /**
41032      * Array of all the buttons that have been added to this form via {@link addButton}
41033      * @type Array
41034      */
41035     this.buttons = [];
41036     this.allItems = [];
41037     this.addEvents({
41038         /**
41039          * @event clientvalidation
41040          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41041          * @param {Form} this
41042          * @param {Boolean} valid true if the form has passed client-side validation
41043          */
41044         clientvalidation: true,
41045         /**
41046          * @event rendered
41047          * Fires when the form is rendered
41048          * @param {Roo.form.Form} form
41049          */
41050         rendered : true
41051     });
41052     
41053     if (this.progressUrl) {
41054             // push a hidden field onto the list of fields..
41055             this.addxtype( {
41056                     xns: Roo.form, 
41057                     xtype : 'Hidden', 
41058                     name : 'UPLOAD_IDENTIFIER' 
41059             });
41060         }
41061         
41062     
41063     Roo.each(xitems, this.addxtype, this);
41064     
41065     
41066     
41067 };
41068
41069 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41070     /**
41071      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41072      */
41073     /**
41074      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41075      */
41076     /**
41077      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41078      */
41079     buttonAlign:'center',
41080
41081     /**
41082      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41083      */
41084     minButtonWidth:75,
41085
41086     /**
41087      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41088      * This property cascades to child containers if not set.
41089      */
41090     labelAlign:'left',
41091
41092     /**
41093      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41094      * fires a looping event with that state. This is required to bind buttons to the valid
41095      * state using the config value formBind:true on the button.
41096      */
41097     monitorValid : false,
41098
41099     /**
41100      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41101      */
41102     monitorPoll : 200,
41103     
41104     /**
41105      * @cfg {String} progressUrl - Url to return progress data 
41106      */
41107     
41108     progressUrl : false,
41109   
41110     /**
41111      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41112      * fields are added and the column is closed. If no fields are passed the column remains open
41113      * until end() is called.
41114      * @param {Object} config The config to pass to the column
41115      * @param {Field} field1 (optional)
41116      * @param {Field} field2 (optional)
41117      * @param {Field} etc (optional)
41118      * @return Column The column container object
41119      */
41120     column : function(c){
41121         var col = new Roo.form.Column(c);
41122         this.start(col);
41123         if(arguments.length > 1){ // duplicate code required because of Opera
41124             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41125             this.end();
41126         }
41127         return col;
41128     },
41129
41130     /**
41131      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41132      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41133      * until end() is called.
41134      * @param {Object} config The config to pass to the fieldset
41135      * @param {Field} field1 (optional)
41136      * @param {Field} field2 (optional)
41137      * @param {Field} etc (optional)
41138      * @return FieldSet The fieldset container object
41139      */
41140     fieldset : function(c){
41141         var fs = new Roo.form.FieldSet(c);
41142         this.start(fs);
41143         if(arguments.length > 1){ // duplicate code required because of Opera
41144             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41145             this.end();
41146         }
41147         return fs;
41148     },
41149
41150     /**
41151      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41152      * fields are added and the container is closed. If no fields are passed the container remains open
41153      * until end() is called.
41154      * @param {Object} config The config to pass to the Layout
41155      * @param {Field} field1 (optional)
41156      * @param {Field} field2 (optional)
41157      * @param {Field} etc (optional)
41158      * @return Layout The container object
41159      */
41160     container : function(c){
41161         var l = new Roo.form.Layout(c);
41162         this.start(l);
41163         if(arguments.length > 1){ // duplicate code required because of Opera
41164             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41165             this.end();
41166         }
41167         return l;
41168     },
41169
41170     /**
41171      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41172      * @param {Object} container A Roo.form.Layout or subclass of Layout
41173      * @return {Form} this
41174      */
41175     start : function(c){
41176         // cascade label info
41177         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41178         this.active.stack.push(c);
41179         c.ownerCt = this.active;
41180         this.active = c;
41181         return this;
41182     },
41183
41184     /**
41185      * Closes the current open container
41186      * @return {Form} this
41187      */
41188     end : function(){
41189         if(this.active == this.root){
41190             return this;
41191         }
41192         this.active = this.active.ownerCt;
41193         return this;
41194     },
41195
41196     /**
41197      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41198      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41199      * as the label of the field.
41200      * @param {Field} field1
41201      * @param {Field} field2 (optional)
41202      * @param {Field} etc. (optional)
41203      * @return {Form} this
41204      */
41205     add : function(){
41206         this.active.stack.push.apply(this.active.stack, arguments);
41207         this.allItems.push.apply(this.allItems,arguments);
41208         var r = [];
41209         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41210             if(a[i].isFormField){
41211                 r.push(a[i]);
41212             }
41213         }
41214         if(r.length > 0){
41215             Roo.form.Form.superclass.add.apply(this, r);
41216         }
41217         return this;
41218     },
41219     
41220
41221     
41222     
41223     
41224      /**
41225      * Find any element that has been added to a form, using it's ID or name
41226      * This can include framesets, columns etc. along with regular fields..
41227      * @param {String} id - id or name to find.
41228      
41229      * @return {Element} e - or false if nothing found.
41230      */
41231     findbyId : function(id)
41232     {
41233         var ret = false;
41234         if (!id) {
41235             return ret;
41236         }
41237         Roo.each(this.allItems, function(f){
41238             if (f.id == id || f.name == id ){
41239                 ret = f;
41240                 return false;
41241             }
41242         });
41243         return ret;
41244     },
41245
41246     
41247     
41248     /**
41249      * Render this form into the passed container. This should only be called once!
41250      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41251      * @return {Form} this
41252      */
41253     render : function(ct)
41254     {
41255         
41256         
41257         
41258         ct = Roo.get(ct);
41259         var o = this.autoCreate || {
41260             tag: 'form',
41261             method : this.method || 'POST',
41262             id : this.id || Roo.id()
41263         };
41264         this.initEl(ct.createChild(o));
41265
41266         this.root.render(this.el);
41267         
41268        
41269              
41270         this.items.each(function(f){
41271             f.render('x-form-el-'+f.id);
41272         });
41273
41274         if(this.buttons.length > 0){
41275             // tables are required to maintain order and for correct IE layout
41276             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41277                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41278                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41279             }}, null, true);
41280             var tr = tb.getElementsByTagName('tr')[0];
41281             for(var i = 0, len = this.buttons.length; i < len; i++) {
41282                 var b = this.buttons[i];
41283                 var td = document.createElement('td');
41284                 td.className = 'x-form-btn-td';
41285                 b.render(tr.appendChild(td));
41286             }
41287         }
41288         if(this.monitorValid){ // initialize after render
41289             this.startMonitoring();
41290         }
41291         this.fireEvent('rendered', this);
41292         return this;
41293     },
41294
41295     /**
41296      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41297      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41298      * object or a valid Roo.DomHelper element config
41299      * @param {Function} handler The function called when the button is clicked
41300      * @param {Object} scope (optional) The scope of the handler function
41301      * @return {Roo.Button}
41302      */
41303     addButton : function(config, handler, scope){
41304         var bc = {
41305             handler: handler,
41306             scope: scope,
41307             minWidth: this.minButtonWidth,
41308             hideParent:true
41309         };
41310         if(typeof config == "string"){
41311             bc.text = config;
41312         }else{
41313             Roo.apply(bc, config);
41314         }
41315         var btn = new Roo.Button(null, bc);
41316         this.buttons.push(btn);
41317         return btn;
41318     },
41319
41320      /**
41321      * Adds a series of form elements (using the xtype property as the factory method.
41322      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41323      * @param {Object} config 
41324      */
41325     
41326     addxtype : function()
41327     {
41328         var ar = Array.prototype.slice.call(arguments, 0);
41329         var ret = false;
41330         for(var i = 0; i < ar.length; i++) {
41331             if (!ar[i]) {
41332                 continue; // skip -- if this happends something invalid got sent, we 
41333                 // should ignore it, as basically that interface element will not show up
41334                 // and that should be pretty obvious!!
41335             }
41336             
41337             if (Roo.form[ar[i].xtype]) {
41338                 ar[i].form = this;
41339                 var fe = Roo.factory(ar[i], Roo.form);
41340                 if (!ret) {
41341                     ret = fe;
41342                 }
41343                 fe.form = this;
41344                 if (fe.store) {
41345                     fe.store.form = this;
41346                 }
41347                 if (fe.isLayout) {  
41348                          
41349                     this.start(fe);
41350                     this.allItems.push(fe);
41351                     if (fe.items && fe.addxtype) {
41352                         fe.addxtype.apply(fe, fe.items);
41353                         delete fe.items;
41354                     }
41355                      this.end();
41356                     continue;
41357                 }
41358                 
41359                 
41360                  
41361                 this.add(fe);
41362               //  console.log('adding ' + ar[i].xtype);
41363             }
41364             if (ar[i].xtype == 'Button') {  
41365                 //console.log('adding button');
41366                 //console.log(ar[i]);
41367                 this.addButton(ar[i]);
41368                 this.allItems.push(fe);
41369                 continue;
41370             }
41371             
41372             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41373                 alert('end is not supported on xtype any more, use items');
41374             //    this.end();
41375             //    //console.log('adding end');
41376             }
41377             
41378         }
41379         return ret;
41380     },
41381     
41382     /**
41383      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41384      * option "monitorValid"
41385      */
41386     startMonitoring : function(){
41387         if(!this.bound){
41388             this.bound = true;
41389             Roo.TaskMgr.start({
41390                 run : this.bindHandler,
41391                 interval : this.monitorPoll || 200,
41392                 scope: this
41393             });
41394         }
41395     },
41396
41397     /**
41398      * Stops monitoring of the valid state of this form
41399      */
41400     stopMonitoring : function(){
41401         this.bound = false;
41402     },
41403
41404     // private
41405     bindHandler : function(){
41406         if(!this.bound){
41407             return false; // stops binding
41408         }
41409         var valid = true;
41410         this.items.each(function(f){
41411             if(!f.isValid(true)){
41412                 valid = false;
41413                 return false;
41414             }
41415         });
41416         for(var i = 0, len = this.buttons.length; i < len; i++){
41417             var btn = this.buttons[i];
41418             if(btn.formBind === true && btn.disabled === valid){
41419                 btn.setDisabled(!valid);
41420             }
41421         }
41422         this.fireEvent('clientvalidation', this, valid);
41423     }
41424     
41425     
41426     
41427     
41428     
41429     
41430     
41431     
41432 });
41433
41434
41435 // back compat
41436 Roo.Form = Roo.form.Form;
41437 /*
41438  * Based on:
41439  * Ext JS Library 1.1.1
41440  * Copyright(c) 2006-2007, Ext JS, LLC.
41441  *
41442  * Originally Released Under LGPL - original licence link has changed is not relivant.
41443  *
41444  * Fork - LGPL
41445  * <script type="text/javascript">
41446  */
41447  
41448  /**
41449  * @class Roo.form.Action
41450  * Internal Class used to handle form actions
41451  * @constructor
41452  * @param {Roo.form.BasicForm} el The form element or its id
41453  * @param {Object} config Configuration options
41454  */
41455  
41456  
41457 // define the action interface
41458 Roo.form.Action = function(form, options){
41459     this.form = form;
41460     this.options = options || {};
41461 };
41462 /**
41463  * Client Validation Failed
41464  * @const 
41465  */
41466 Roo.form.Action.CLIENT_INVALID = 'client';
41467 /**
41468  * Server Validation Failed
41469  * @const 
41470  */
41471  Roo.form.Action.SERVER_INVALID = 'server';
41472  /**
41473  * Connect to Server Failed
41474  * @const 
41475  */
41476 Roo.form.Action.CONNECT_FAILURE = 'connect';
41477 /**
41478  * Reading Data from Server Failed
41479  * @const 
41480  */
41481 Roo.form.Action.LOAD_FAILURE = 'load';
41482
41483 Roo.form.Action.prototype = {
41484     type : 'default',
41485     failureType : undefined,
41486     response : undefined,
41487     result : undefined,
41488
41489     // interface method
41490     run : function(options){
41491
41492     },
41493
41494     // interface method
41495     success : function(response){
41496
41497     },
41498
41499     // interface method
41500     handleResponse : function(response){
41501
41502     },
41503
41504     // default connection failure
41505     failure : function(response){
41506         
41507         this.response = response;
41508         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41509         this.form.afterAction(this, false);
41510     },
41511
41512     processResponse : function(response){
41513         this.response = response;
41514         if(!response.responseText){
41515             return true;
41516         }
41517         this.result = this.handleResponse(response);
41518         return this.result;
41519     },
41520
41521     // utility functions used internally
41522     getUrl : function(appendParams){
41523         var url = this.options.url || this.form.url || this.form.el.dom.action;
41524         if(appendParams){
41525             var p = this.getParams();
41526             if(p){
41527                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
41528             }
41529         }
41530         return url;
41531     },
41532
41533     getMethod : function(){
41534         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
41535     },
41536
41537     getParams : function(){
41538         var bp = this.form.baseParams;
41539         var p = this.options.params;
41540         if(p){
41541             if(typeof p == "object"){
41542                 p = Roo.urlEncode(Roo.applyIf(p, bp));
41543             }else if(typeof p == 'string' && bp){
41544                 p += '&' + Roo.urlEncode(bp);
41545             }
41546         }else if(bp){
41547             p = Roo.urlEncode(bp);
41548         }
41549         return p;
41550     },
41551
41552     createCallback : function(){
41553         return {
41554             success: this.success,
41555             failure: this.failure,
41556             scope: this,
41557             timeout: (this.form.timeout*1000),
41558             upload: this.form.fileUpload ? this.success : undefined
41559         };
41560     }
41561 };
41562
41563 Roo.form.Action.Submit = function(form, options){
41564     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
41565 };
41566
41567 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
41568     type : 'submit',
41569
41570     haveProgress : false,
41571     uploadComplete : false,
41572     
41573     // uploadProgress indicator.
41574     uploadProgress : function()
41575     {
41576         if (!this.form.progressUrl) {
41577             return;
41578         }
41579         
41580         if (!this.haveProgress) {
41581             Roo.MessageBox.progress("Uploading", "Uploading");
41582         }
41583         if (this.uploadComplete) {
41584            Roo.MessageBox.hide();
41585            return;
41586         }
41587         
41588         this.haveProgress = true;
41589    
41590         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
41591         
41592         var c = new Roo.data.Connection();
41593         c.request({
41594             url : this.form.progressUrl,
41595             params: {
41596                 id : uid
41597             },
41598             method: 'GET',
41599             success : function(req){
41600                //console.log(data);
41601                 var rdata = false;
41602                 var edata;
41603                 try  {
41604                    rdata = Roo.decode(req.responseText)
41605                 } catch (e) {
41606                     Roo.log("Invalid data from server..");
41607                     Roo.log(edata);
41608                     return;
41609                 }
41610                 if (!rdata || !rdata.success) {
41611                     Roo.log(rdata);
41612                     return;
41613                 }
41614                 var data = rdata.data;
41615                 
41616                 if (this.uploadComplete) {
41617                    Roo.MessageBox.hide();
41618                    return;
41619                 }
41620                    
41621                 if (data){
41622                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
41623                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
41624                     );
41625                 }
41626                 this.uploadProgress.defer(2000,this);
41627             },
41628        
41629             failure: function(data) {
41630                 Roo.log('progress url failed ');
41631                 Roo.log(data);
41632             },
41633             scope : this
41634         });
41635            
41636     },
41637     
41638     
41639     run : function()
41640     {
41641         // run get Values on the form, so it syncs any secondary forms.
41642         this.form.getValues();
41643         
41644         var o = this.options;
41645         var method = this.getMethod();
41646         var isPost = method == 'POST';
41647         if(o.clientValidation === false || this.form.isValid()){
41648             
41649             if (this.form.progressUrl) {
41650                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
41651                     (new Date() * 1) + '' + Math.random());
41652                     
41653             } 
41654             
41655             
41656             Roo.Ajax.request(Roo.apply(this.createCallback(), {
41657                 form:this.form.el.dom,
41658                 url:this.getUrl(!isPost),
41659                 method: method,
41660                 params:isPost ? this.getParams() : null,
41661                 isUpload: this.form.fileUpload
41662             }));
41663             
41664             this.uploadProgress();
41665
41666         }else if (o.clientValidation !== false){ // client validation failed
41667             this.failureType = Roo.form.Action.CLIENT_INVALID;
41668             this.form.afterAction(this, false);
41669         }
41670     },
41671
41672     success : function(response)
41673     {
41674         this.uploadComplete= true;
41675         if (this.haveProgress) {
41676             Roo.MessageBox.hide();
41677         }
41678         
41679         
41680         var result = this.processResponse(response);
41681         if(result === true || result.success){
41682             this.form.afterAction(this, true);
41683             return;
41684         }
41685         if(result.errors){
41686             this.form.markInvalid(result.errors);
41687             this.failureType = Roo.form.Action.SERVER_INVALID;
41688         }
41689         this.form.afterAction(this, false);
41690     },
41691     failure : function(response)
41692     {
41693         this.uploadComplete= true;
41694         if (this.haveProgress) {
41695             Roo.MessageBox.hide();
41696         }
41697         
41698         
41699         this.response = response;
41700         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41701         this.form.afterAction(this, false);
41702     },
41703     
41704     handleResponse : function(response){
41705         if(this.form.errorReader){
41706             var rs = this.form.errorReader.read(response);
41707             var errors = [];
41708             if(rs.records){
41709                 for(var i = 0, len = rs.records.length; i < len; i++) {
41710                     var r = rs.records[i];
41711                     errors[i] = r.data;
41712                 }
41713             }
41714             if(errors.length < 1){
41715                 errors = null;
41716             }
41717             return {
41718                 success : rs.success,
41719                 errors : errors
41720             };
41721         }
41722         var ret = false;
41723         try {
41724             ret = Roo.decode(response.responseText);
41725         } catch (e) {
41726             ret = {
41727                 success: false,
41728                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
41729                 errors : []
41730             };
41731         }
41732         return ret;
41733         
41734     }
41735 });
41736
41737
41738 Roo.form.Action.Load = function(form, options){
41739     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
41740     this.reader = this.form.reader;
41741 };
41742
41743 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
41744     type : 'load',
41745
41746     run : function(){
41747         
41748         Roo.Ajax.request(Roo.apply(
41749                 this.createCallback(), {
41750                     method:this.getMethod(),
41751                     url:this.getUrl(false),
41752                     params:this.getParams()
41753         }));
41754     },
41755
41756     success : function(response){
41757         
41758         var result = this.processResponse(response);
41759         if(result === true || !result.success || !result.data){
41760             this.failureType = Roo.form.Action.LOAD_FAILURE;
41761             this.form.afterAction(this, false);
41762             return;
41763         }
41764         this.form.clearInvalid();
41765         this.form.setValues(result.data);
41766         this.form.afterAction(this, true);
41767     },
41768
41769     handleResponse : function(response){
41770         if(this.form.reader){
41771             var rs = this.form.reader.read(response);
41772             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
41773             return {
41774                 success : rs.success,
41775                 data : data
41776             };
41777         }
41778         return Roo.decode(response.responseText);
41779     }
41780 });
41781
41782 Roo.form.Action.ACTION_TYPES = {
41783     'load' : Roo.form.Action.Load,
41784     'submit' : Roo.form.Action.Submit
41785 };/*
41786  * Based on:
41787  * Ext JS Library 1.1.1
41788  * Copyright(c) 2006-2007, Ext JS, LLC.
41789  *
41790  * Originally Released Under LGPL - original licence link has changed is not relivant.
41791  *
41792  * Fork - LGPL
41793  * <script type="text/javascript">
41794  */
41795  
41796 /**
41797  * @class Roo.form.Layout
41798  * @extends Roo.Component
41799  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
41800  * @constructor
41801  * @param {Object} config Configuration options
41802  */
41803 Roo.form.Layout = function(config){
41804     var xitems = [];
41805     if (config.items) {
41806         xitems = config.items;
41807         delete config.items;
41808     }
41809     Roo.form.Layout.superclass.constructor.call(this, config);
41810     this.stack = [];
41811     Roo.each(xitems, this.addxtype, this);
41812      
41813 };
41814
41815 Roo.extend(Roo.form.Layout, Roo.Component, {
41816     /**
41817      * @cfg {String/Object} autoCreate
41818      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
41819      */
41820     /**
41821      * @cfg {String/Object/Function} style
41822      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
41823      * a function which returns such a specification.
41824      */
41825     /**
41826      * @cfg {String} labelAlign
41827      * Valid values are "left," "top" and "right" (defaults to "left")
41828      */
41829     /**
41830      * @cfg {Number} labelWidth
41831      * Fixed width in pixels of all field labels (defaults to undefined)
41832      */
41833     /**
41834      * @cfg {Boolean} clear
41835      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
41836      */
41837     clear : true,
41838     /**
41839      * @cfg {String} labelSeparator
41840      * The separator to use after field labels (defaults to ':')
41841      */
41842     labelSeparator : ':',
41843     /**
41844      * @cfg {Boolean} hideLabels
41845      * True to suppress the display of field labels in this layout (defaults to false)
41846      */
41847     hideLabels : false,
41848
41849     // private
41850     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
41851     
41852     isLayout : true,
41853     
41854     // private
41855     onRender : function(ct, position){
41856         if(this.el){ // from markup
41857             this.el = Roo.get(this.el);
41858         }else {  // generate
41859             var cfg = this.getAutoCreate();
41860             this.el = ct.createChild(cfg, position);
41861         }
41862         if(this.style){
41863             this.el.applyStyles(this.style);
41864         }
41865         if(this.labelAlign){
41866             this.el.addClass('x-form-label-'+this.labelAlign);
41867         }
41868         if(this.hideLabels){
41869             this.labelStyle = "display:none";
41870             this.elementStyle = "padding-left:0;";
41871         }else{
41872             if(typeof this.labelWidth == 'number'){
41873                 this.labelStyle = "width:"+this.labelWidth+"px;";
41874                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
41875             }
41876             if(this.labelAlign == 'top'){
41877                 this.labelStyle = "width:auto;";
41878                 this.elementStyle = "padding-left:0;";
41879             }
41880         }
41881         var stack = this.stack;
41882         var slen = stack.length;
41883         if(slen > 0){
41884             if(!this.fieldTpl){
41885                 var t = new Roo.Template(
41886                     '<div class="x-form-item {5}">',
41887                         '<label for="{0}" style="{2}">{1}{4}</label>',
41888                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41889                         '</div>',
41890                     '</div><div class="x-form-clear-left"></div>'
41891                 );
41892                 t.disableFormats = true;
41893                 t.compile();
41894                 Roo.form.Layout.prototype.fieldTpl = t;
41895             }
41896             for(var i = 0; i < slen; i++) {
41897                 if(stack[i].isFormField){
41898                     this.renderField(stack[i]);
41899                 }else{
41900                     this.renderComponent(stack[i]);
41901                 }
41902             }
41903         }
41904         if(this.clear){
41905             this.el.createChild({cls:'x-form-clear'});
41906         }
41907     },
41908
41909     // private
41910     renderField : function(f){
41911         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
41912                f.id, //0
41913                f.fieldLabel, //1
41914                f.labelStyle||this.labelStyle||'', //2
41915                this.elementStyle||'', //3
41916                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
41917                f.itemCls||this.itemCls||''  //5
41918        ], true).getPrevSibling());
41919     },
41920
41921     // private
41922     renderComponent : function(c){
41923         c.render(c.isLayout ? this.el : this.el.createChild());    
41924     },
41925     /**
41926      * Adds a object form elements (using the xtype property as the factory method.)
41927      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
41928      * @param {Object} config 
41929      */
41930     addxtype : function(o)
41931     {
41932         // create the lement.
41933         o.form = this.form;
41934         var fe = Roo.factory(o, Roo.form);
41935         this.form.allItems.push(fe);
41936         this.stack.push(fe);
41937         
41938         if (fe.isFormField) {
41939             this.form.items.add(fe);
41940         }
41941          
41942         return fe;
41943     }
41944 });
41945
41946 /**
41947  * @class Roo.form.Column
41948  * @extends Roo.form.Layout
41949  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
41950  * @constructor
41951  * @param {Object} config Configuration options
41952  */
41953 Roo.form.Column = function(config){
41954     Roo.form.Column.superclass.constructor.call(this, config);
41955 };
41956
41957 Roo.extend(Roo.form.Column, Roo.form.Layout, {
41958     /**
41959      * @cfg {Number/String} width
41960      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41961      */
41962     /**
41963      * @cfg {String/Object} autoCreate
41964      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
41965      */
41966
41967     // private
41968     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
41969
41970     // private
41971     onRender : function(ct, position){
41972         Roo.form.Column.superclass.onRender.call(this, ct, position);
41973         if(this.width){
41974             this.el.setWidth(this.width);
41975         }
41976     }
41977 });
41978
41979
41980 /**
41981  * @class Roo.form.Row
41982  * @extends Roo.form.Layout
41983  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
41984  * @constructor
41985  * @param {Object} config Configuration options
41986  */
41987
41988  
41989 Roo.form.Row = function(config){
41990     Roo.form.Row.superclass.constructor.call(this, config);
41991 };
41992  
41993 Roo.extend(Roo.form.Row, Roo.form.Layout, {
41994       /**
41995      * @cfg {Number/String} width
41996      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41997      */
41998     /**
41999      * @cfg {Number/String} height
42000      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42001      */
42002     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42003     
42004     padWidth : 20,
42005     // private
42006     onRender : function(ct, position){
42007         //console.log('row render');
42008         if(!this.rowTpl){
42009             var t = new Roo.Template(
42010                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42011                     '<label for="{0}" style="{2}">{1}{4}</label>',
42012                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42013                     '</div>',
42014                 '</div>'
42015             );
42016             t.disableFormats = true;
42017             t.compile();
42018             Roo.form.Layout.prototype.rowTpl = t;
42019         }
42020         this.fieldTpl = this.rowTpl;
42021         
42022         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42023         var labelWidth = 100;
42024         
42025         if ((this.labelAlign != 'top')) {
42026             if (typeof this.labelWidth == 'number') {
42027                 labelWidth = this.labelWidth
42028             }
42029             this.padWidth =  20 + labelWidth;
42030             
42031         }
42032         
42033         Roo.form.Column.superclass.onRender.call(this, ct, position);
42034         if(this.width){
42035             this.el.setWidth(this.width);
42036         }
42037         if(this.height){
42038             this.el.setHeight(this.height);
42039         }
42040     },
42041     
42042     // private
42043     renderField : function(f){
42044         f.fieldEl = this.fieldTpl.append(this.el, [
42045                f.id, f.fieldLabel,
42046                f.labelStyle||this.labelStyle||'',
42047                this.elementStyle||'',
42048                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42049                f.itemCls||this.itemCls||'',
42050                f.width ? f.width + this.padWidth : 160 + this.padWidth
42051        ],true);
42052     }
42053 });
42054  
42055
42056 /**
42057  * @class Roo.form.FieldSet
42058  * @extends Roo.form.Layout
42059  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42060  * @constructor
42061  * @param {Object} config Configuration options
42062  */
42063 Roo.form.FieldSet = function(config){
42064     Roo.form.FieldSet.superclass.constructor.call(this, config);
42065 };
42066
42067 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42068     /**
42069      * @cfg {String} legend
42070      * The text to display as the legend for the FieldSet (defaults to '')
42071      */
42072     /**
42073      * @cfg {String/Object} autoCreate
42074      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42075      */
42076
42077     // private
42078     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42079
42080     // private
42081     onRender : function(ct, position){
42082         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42083         if(this.legend){
42084             this.setLegend(this.legend);
42085         }
42086     },
42087
42088     // private
42089     setLegend : function(text){
42090         if(this.rendered){
42091             this.el.child('legend').update(text);
42092         }
42093     }
42094 });/*
42095  * Based on:
42096  * Ext JS Library 1.1.1
42097  * Copyright(c) 2006-2007, Ext JS, LLC.
42098  *
42099  * Originally Released Under LGPL - original licence link has changed is not relivant.
42100  *
42101  * Fork - LGPL
42102  * <script type="text/javascript">
42103  */
42104 /**
42105  * @class Roo.form.VTypes
42106  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42107  * @singleton
42108  */
42109 Roo.form.VTypes = function(){
42110     // closure these in so they are only created once.
42111     var alpha = /^[a-zA-Z_]+$/;
42112     var alphanum = /^[a-zA-Z0-9_]+$/;
42113     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42114     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42115
42116     // All these messages and functions are configurable
42117     return {
42118         /**
42119          * The function used to validate email addresses
42120          * @param {String} value The email address
42121          */
42122         'email' : function(v){
42123             return email.test(v);
42124         },
42125         /**
42126          * The error text to display when the email validation function returns false
42127          * @type String
42128          */
42129         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42130         /**
42131          * The keystroke filter mask to be applied on email input
42132          * @type RegExp
42133          */
42134         'emailMask' : /[a-z0-9_\.\-@]/i,
42135
42136         /**
42137          * The function used to validate URLs
42138          * @param {String} value The URL
42139          */
42140         'url' : function(v){
42141             return url.test(v);
42142         },
42143         /**
42144          * The error text to display when the url validation function returns false
42145          * @type String
42146          */
42147         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42148         
42149         /**
42150          * The function used to validate alpha values
42151          * @param {String} value The value
42152          */
42153         'alpha' : function(v){
42154             return alpha.test(v);
42155         },
42156         /**
42157          * The error text to display when the alpha validation function returns false
42158          * @type String
42159          */
42160         'alphaText' : 'This field should only contain letters and _',
42161         /**
42162          * The keystroke filter mask to be applied on alpha input
42163          * @type RegExp
42164          */
42165         'alphaMask' : /[a-z_]/i,
42166
42167         /**
42168          * The function used to validate alphanumeric values
42169          * @param {String} value The value
42170          */
42171         'alphanum' : function(v){
42172             return alphanum.test(v);
42173         },
42174         /**
42175          * The error text to display when the alphanumeric validation function returns false
42176          * @type String
42177          */
42178         'alphanumText' : 'This field should only contain letters, numbers and _',
42179         /**
42180          * The keystroke filter mask to be applied on alphanumeric input
42181          * @type RegExp
42182          */
42183         'alphanumMask' : /[a-z0-9_]/i
42184     };
42185 }();//<script type="text/javascript">
42186
42187 /**
42188  * @class Roo.form.FCKeditor
42189  * @extends Roo.form.TextArea
42190  * Wrapper around the FCKEditor http://www.fckeditor.net
42191  * @constructor
42192  * Creates a new FCKeditor
42193  * @param {Object} config Configuration options
42194  */
42195 Roo.form.FCKeditor = function(config){
42196     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42197     this.addEvents({
42198          /**
42199          * @event editorinit
42200          * Fired when the editor is initialized - you can add extra handlers here..
42201          * @param {FCKeditor} this
42202          * @param {Object} the FCK object.
42203          */
42204         editorinit : true
42205     });
42206     
42207     
42208 };
42209 Roo.form.FCKeditor.editors = { };
42210 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42211 {
42212     //defaultAutoCreate : {
42213     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42214     //},
42215     // private
42216     /**
42217      * @cfg {Object} fck options - see fck manual for details.
42218      */
42219     fckconfig : false,
42220     
42221     /**
42222      * @cfg {Object} fck toolbar set (Basic or Default)
42223      */
42224     toolbarSet : 'Basic',
42225     /**
42226      * @cfg {Object} fck BasePath
42227      */ 
42228     basePath : '/fckeditor/',
42229     
42230     
42231     frame : false,
42232     
42233     value : '',
42234     
42235    
42236     onRender : function(ct, position)
42237     {
42238         if(!this.el){
42239             this.defaultAutoCreate = {
42240                 tag: "textarea",
42241                 style:"width:300px;height:60px;",
42242                 autocomplete: "off"
42243             };
42244         }
42245         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42246         /*
42247         if(this.grow){
42248             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42249             if(this.preventScrollbars){
42250                 this.el.setStyle("overflow", "hidden");
42251             }
42252             this.el.setHeight(this.growMin);
42253         }
42254         */
42255         //console.log('onrender' + this.getId() );
42256         Roo.form.FCKeditor.editors[this.getId()] = this;
42257          
42258
42259         this.replaceTextarea() ;
42260         
42261     },
42262     
42263     getEditor : function() {
42264         return this.fckEditor;
42265     },
42266     /**
42267      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42268      * @param {Mixed} value The value to set
42269      */
42270     
42271     
42272     setValue : function(value)
42273     {
42274         //console.log('setValue: ' + value);
42275         
42276         if(typeof(value) == 'undefined') { // not sure why this is happending...
42277             return;
42278         }
42279         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42280         
42281         //if(!this.el || !this.getEditor()) {
42282         //    this.value = value;
42283             //this.setValue.defer(100,this,[value]);    
42284         //    return;
42285         //} 
42286         
42287         if(!this.getEditor()) {
42288             return;
42289         }
42290         
42291         this.getEditor().SetData(value);
42292         
42293         //
42294
42295     },
42296
42297     /**
42298      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42299      * @return {Mixed} value The field value
42300      */
42301     getValue : function()
42302     {
42303         
42304         if (this.frame && this.frame.dom.style.display == 'none') {
42305             return Roo.form.FCKeditor.superclass.getValue.call(this);
42306         }
42307         
42308         if(!this.el || !this.getEditor()) {
42309            
42310            // this.getValue.defer(100,this); 
42311             return this.value;
42312         }
42313        
42314         
42315         var value=this.getEditor().GetData();
42316         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42317         return Roo.form.FCKeditor.superclass.getValue.call(this);
42318         
42319
42320     },
42321
42322     /**
42323      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42324      * @return {Mixed} value The field value
42325      */
42326     getRawValue : function()
42327     {
42328         if (this.frame && this.frame.dom.style.display == 'none') {
42329             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42330         }
42331         
42332         if(!this.el || !this.getEditor()) {
42333             //this.getRawValue.defer(100,this); 
42334             return this.value;
42335             return;
42336         }
42337         
42338         
42339         
42340         var value=this.getEditor().GetData();
42341         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42342         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42343          
42344     },
42345     
42346     setSize : function(w,h) {
42347         
42348         
42349         
42350         //if (this.frame && this.frame.dom.style.display == 'none') {
42351         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42352         //    return;
42353         //}
42354         //if(!this.el || !this.getEditor()) {
42355         //    this.setSize.defer(100,this, [w,h]); 
42356         //    return;
42357         //}
42358         
42359         
42360         
42361         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42362         
42363         this.frame.dom.setAttribute('width', w);
42364         this.frame.dom.setAttribute('height', h);
42365         this.frame.setSize(w,h);
42366         
42367     },
42368     
42369     toggleSourceEdit : function(value) {
42370         
42371       
42372          
42373         this.el.dom.style.display = value ? '' : 'none';
42374         this.frame.dom.style.display = value ?  'none' : '';
42375         
42376     },
42377     
42378     
42379     focus: function(tag)
42380     {
42381         if (this.frame.dom.style.display == 'none') {
42382             return Roo.form.FCKeditor.superclass.focus.call(this);
42383         }
42384         if(!this.el || !this.getEditor()) {
42385             this.focus.defer(100,this, [tag]); 
42386             return;
42387         }
42388         
42389         
42390         
42391         
42392         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
42393         this.getEditor().Focus();
42394         if (tgs.length) {
42395             if (!this.getEditor().Selection.GetSelection()) {
42396                 this.focus.defer(100,this, [tag]); 
42397                 return;
42398             }
42399             
42400             
42401             var r = this.getEditor().EditorDocument.createRange();
42402             r.setStart(tgs[0],0);
42403             r.setEnd(tgs[0],0);
42404             this.getEditor().Selection.GetSelection().removeAllRanges();
42405             this.getEditor().Selection.GetSelection().addRange(r);
42406             this.getEditor().Focus();
42407         }
42408         
42409     },
42410     
42411     
42412     
42413     replaceTextarea : function()
42414     {
42415         if ( document.getElementById( this.getId() + '___Frame' ) )
42416             return ;
42417         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
42418         //{
42419             // We must check the elements firstly using the Id and then the name.
42420         var oTextarea = document.getElementById( this.getId() );
42421         
42422         var colElementsByName = document.getElementsByName( this.getId() ) ;
42423          
42424         oTextarea.style.display = 'none' ;
42425
42426         if ( oTextarea.tabIndex ) {            
42427             this.TabIndex = oTextarea.tabIndex ;
42428         }
42429         
42430         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
42431         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
42432         this.frame = Roo.get(this.getId() + '___Frame')
42433     },
42434     
42435     _getConfigHtml : function()
42436     {
42437         var sConfig = '' ;
42438
42439         for ( var o in this.fckconfig ) {
42440             sConfig += sConfig.length > 0  ? '&amp;' : '';
42441             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
42442         }
42443
42444         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
42445     },
42446     
42447     
42448     _getIFrameHtml : function()
42449     {
42450         var sFile = 'fckeditor.html' ;
42451         /* no idea what this is about..
42452         try
42453         {
42454             if ( (/fcksource=true/i).test( window.top.location.search ) )
42455                 sFile = 'fckeditor.original.html' ;
42456         }
42457         catch (e) { 
42458         */
42459
42460         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
42461         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
42462         
42463         
42464         var html = '<iframe id="' + this.getId() +
42465             '___Frame" src="' + sLink +
42466             '" width="' + this.width +
42467             '" height="' + this.height + '"' +
42468             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
42469             ' frameborder="0" scrolling="no"></iframe>' ;
42470
42471         return html ;
42472     },
42473     
42474     _insertHtmlBefore : function( html, element )
42475     {
42476         if ( element.insertAdjacentHTML )       {
42477             // IE
42478             element.insertAdjacentHTML( 'beforeBegin', html ) ;
42479         } else { // Gecko
42480             var oRange = document.createRange() ;
42481             oRange.setStartBefore( element ) ;
42482             var oFragment = oRange.createContextualFragment( html );
42483             element.parentNode.insertBefore( oFragment, element ) ;
42484         }
42485     }
42486     
42487     
42488   
42489     
42490     
42491     
42492     
42493
42494 });
42495
42496 //Roo.reg('fckeditor', Roo.form.FCKeditor);
42497
42498 function FCKeditor_OnComplete(editorInstance){
42499     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
42500     f.fckEditor = editorInstance;
42501     //console.log("loaded");
42502     f.fireEvent('editorinit', f, editorInstance);
42503
42504   
42505
42506  
42507
42508
42509
42510
42511
42512
42513
42514
42515
42516
42517
42518
42519
42520
42521
42522 //<script type="text/javascript">
42523 /**
42524  * @class Roo.form.GridField
42525  * @extends Roo.form.Field
42526  * Embed a grid (or editable grid into a form)
42527  * STATUS ALPHA
42528  * 
42529  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
42530  * it needs 
42531  * xgrid.store = Roo.data.Store
42532  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
42533  * xgrid.store.reader = Roo.data.JsonReader 
42534  * 
42535  * 
42536  * @constructor
42537  * Creates a new GridField
42538  * @param {Object} config Configuration options
42539  */
42540 Roo.form.GridField = function(config){
42541     Roo.form.GridField.superclass.constructor.call(this, config);
42542      
42543 };
42544
42545 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
42546     /**
42547      * @cfg {Number} width  - used to restrict width of grid..
42548      */
42549     width : 100,
42550     /**
42551      * @cfg {Number} height - used to restrict height of grid..
42552      */
42553     height : 50,
42554      /**
42555      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
42556          * 
42557          *}
42558      */
42559     xgrid : false, 
42560     /**
42561      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42562      * {tag: "input", type: "checkbox", autocomplete: "off"})
42563      */
42564    // defaultAutoCreate : { tag: 'div' },
42565     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42566     /**
42567      * @cfg {String} addTitle Text to include for adding a title.
42568      */
42569     addTitle : false,
42570     //
42571     onResize : function(){
42572         Roo.form.Field.superclass.onResize.apply(this, arguments);
42573     },
42574
42575     initEvents : function(){
42576         // Roo.form.Checkbox.superclass.initEvents.call(this);
42577         // has no events...
42578        
42579     },
42580
42581
42582     getResizeEl : function(){
42583         return this.wrap;
42584     },
42585
42586     getPositionEl : function(){
42587         return this.wrap;
42588     },
42589
42590     // private
42591     onRender : function(ct, position){
42592         
42593         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
42594         var style = this.style;
42595         delete this.style;
42596         
42597         Roo.form.GridField.superclass.onRender.call(this, ct, position);
42598         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
42599         this.viewEl = this.wrap.createChild({ tag: 'div' });
42600         if (style) {
42601             this.viewEl.applyStyles(style);
42602         }
42603         if (this.width) {
42604             this.viewEl.setWidth(this.width);
42605         }
42606         if (this.height) {
42607             this.viewEl.setHeight(this.height);
42608         }
42609         //if(this.inputValue !== undefined){
42610         //this.setValue(this.value);
42611         
42612         
42613         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
42614         
42615         
42616         this.grid.render();
42617         this.grid.getDataSource().on('remove', this.refreshValue, this);
42618         this.grid.getDataSource().on('update', this.refreshValue, this);
42619         this.grid.on('afteredit', this.refreshValue, this);
42620  
42621     },
42622      
42623     
42624     /**
42625      * Sets the value of the item. 
42626      * @param {String} either an object  or a string..
42627      */
42628     setValue : function(v){
42629         //this.value = v;
42630         v = v || []; // empty set..
42631         // this does not seem smart - it really only affects memoryproxy grids..
42632         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
42633             var ds = this.grid.getDataSource();
42634             // assumes a json reader..
42635             var data = {}
42636             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
42637             ds.loadData( data);
42638         }
42639         Roo.form.GridField.superclass.setValue.call(this, v);
42640         this.refreshValue();
42641         // should load data in the grid really....
42642     },
42643     
42644     // private
42645     refreshValue: function() {
42646          var val = [];
42647         this.grid.getDataSource().each(function(r) {
42648             val.push(r.data);
42649         });
42650         this.el.dom.value = Roo.encode(val);
42651     }
42652     
42653      
42654     
42655     
42656 });/*
42657  * Based on:
42658  * Ext JS Library 1.1.1
42659  * Copyright(c) 2006-2007, Ext JS, LLC.
42660  *
42661  * Originally Released Under LGPL - original licence link has changed is not relivant.
42662  *
42663  * Fork - LGPL
42664  * <script type="text/javascript">
42665  */
42666 /**
42667  * @class Roo.form.DisplayField
42668  * @extends Roo.form.Field
42669  * A generic Field to display non-editable data.
42670  * @constructor
42671  * Creates a new Display Field item.
42672  * @param {Object} config Configuration options
42673  */
42674 Roo.form.DisplayField = function(config){
42675     Roo.form.DisplayField.superclass.constructor.call(this, config);
42676     
42677 };
42678
42679 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
42680     inputType:      'hidden',
42681     allowBlank:     true,
42682     readOnly:         true,
42683     
42684  
42685     /**
42686      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42687      */
42688     focusClass : undefined,
42689     /**
42690      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42691      */
42692     fieldClass: 'x-form-field',
42693     
42694      /**
42695      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
42696      */
42697     valueRenderer: undefined,
42698     
42699     width: 100,
42700     /**
42701      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42702      * {tag: "input", type: "checkbox", autocomplete: "off"})
42703      */
42704      
42705  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42706
42707     onResize : function(){
42708         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
42709         
42710     },
42711
42712     initEvents : function(){
42713         // Roo.form.Checkbox.superclass.initEvents.call(this);
42714         // has no events...
42715        
42716     },
42717
42718
42719     getResizeEl : function(){
42720         return this.wrap;
42721     },
42722
42723     getPositionEl : function(){
42724         return this.wrap;
42725     },
42726
42727     // private
42728     onRender : function(ct, position){
42729         
42730         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
42731         //if(this.inputValue !== undefined){
42732         this.wrap = this.el.wrap();
42733         
42734         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
42735         
42736         if (this.bodyStyle) {
42737             this.viewEl.applyStyles(this.bodyStyle);
42738         }
42739         //this.viewEl.setStyle('padding', '2px');
42740         
42741         this.setValue(this.value);
42742         
42743     },
42744 /*
42745     // private
42746     initValue : Roo.emptyFn,
42747
42748   */
42749
42750         // private
42751     onClick : function(){
42752         
42753     },
42754
42755     /**
42756      * Sets the checked state of the checkbox.
42757      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
42758      */
42759     setValue : function(v){
42760         this.value = v;
42761         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
42762         // this might be called before we have a dom element..
42763         if (!this.viewEl) {
42764             return;
42765         }
42766         this.viewEl.dom.innerHTML = html;
42767         Roo.form.DisplayField.superclass.setValue.call(this, v);
42768
42769     }
42770 });/*
42771  * 
42772  * Licence- LGPL
42773  * 
42774  */
42775
42776 /**
42777  * @class Roo.form.DayPicker
42778  * @extends Roo.form.Field
42779  * A Day picker show [M] [T] [W] ....
42780  * @constructor
42781  * Creates a new Day Picker
42782  * @param {Object} config Configuration options
42783  */
42784 Roo.form.DayPicker= function(config){
42785     Roo.form.DayPicker.superclass.constructor.call(this, config);
42786      
42787 };
42788
42789 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
42790     /**
42791      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42792      */
42793     focusClass : undefined,
42794     /**
42795      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42796      */
42797     fieldClass: "x-form-field",
42798    
42799     /**
42800      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42801      * {tag: "input", type: "checkbox", autocomplete: "off"})
42802      */
42803     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42804     
42805    
42806     actionMode : 'viewEl', 
42807     //
42808     // private
42809  
42810     inputType : 'hidden',
42811     
42812      
42813     inputElement: false, // real input element?
42814     basedOn: false, // ????
42815     
42816     isFormField: true, // not sure where this is needed!!!!
42817
42818     onResize : function(){
42819         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42820         if(!this.boxLabel){
42821             this.el.alignTo(this.wrap, 'c-c');
42822         }
42823     },
42824
42825     initEvents : function(){
42826         Roo.form.Checkbox.superclass.initEvents.call(this);
42827         this.el.on("click", this.onClick,  this);
42828         this.el.on("change", this.onClick,  this);
42829     },
42830
42831
42832     getResizeEl : function(){
42833         return this.wrap;
42834     },
42835
42836     getPositionEl : function(){
42837         return this.wrap;
42838     },
42839
42840     
42841     // private
42842     onRender : function(ct, position){
42843         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42844        
42845         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
42846         
42847         var r1 = '<table><tr>';
42848         var r2 = '<tr class="x-form-daypick-icons">';
42849         for (var i=0; i < 7; i++) {
42850             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
42851             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
42852         }
42853         
42854         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
42855         viewEl.select('img').on('click', this.onClick, this);
42856         this.viewEl = viewEl;   
42857         
42858         
42859         // this will not work on Chrome!!!
42860         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42861         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42862         
42863         
42864           
42865
42866     },
42867
42868     // private
42869     initValue : Roo.emptyFn,
42870
42871     /**
42872      * Returns the checked state of the checkbox.
42873      * @return {Boolean} True if checked, else false
42874      */
42875     getValue : function(){
42876         return this.el.dom.value;
42877         
42878     },
42879
42880         // private
42881     onClick : function(e){ 
42882         //this.setChecked(!this.checked);
42883         Roo.get(e.target).toggleClass('x-menu-item-checked');
42884         this.refreshValue();
42885         //if(this.el.dom.checked != this.checked){
42886         //    this.setValue(this.el.dom.checked);
42887        // }
42888     },
42889     
42890     // private
42891     refreshValue : function()
42892     {
42893         var val = '';
42894         this.viewEl.select('img',true).each(function(e,i,n)  {
42895             val += e.is(".x-menu-item-checked") ? String(n) : '';
42896         });
42897         this.setValue(val, true);
42898     },
42899
42900     /**
42901      * Sets the checked state of the checkbox.
42902      * On is always based on a string comparison between inputValue and the param.
42903      * @param {Boolean/String} value - the value to set 
42904      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42905      */
42906     setValue : function(v,suppressEvent){
42907         if (!this.el.dom) {
42908             return;
42909         }
42910         var old = this.el.dom.value ;
42911         this.el.dom.value = v;
42912         if (suppressEvent) {
42913             return ;
42914         }
42915          
42916         // update display..
42917         this.viewEl.select('img',true).each(function(e,i,n)  {
42918             
42919             var on = e.is(".x-menu-item-checked");
42920             var newv = v.indexOf(String(n)) > -1;
42921             if (on != newv) {
42922                 e.toggleClass('x-menu-item-checked');
42923             }
42924             
42925         });
42926         
42927         
42928         this.fireEvent('change', this, v, old);
42929         
42930         
42931     },
42932    
42933     // handle setting of hidden value by some other method!!?!?
42934     setFromHidden: function()
42935     {
42936         if(!this.el){
42937             return;
42938         }
42939         //console.log("SET FROM HIDDEN");
42940         //alert('setFrom hidden');
42941         this.setValue(this.el.dom.value);
42942     },
42943     
42944     onDestroy : function()
42945     {
42946         if(this.viewEl){
42947             Roo.get(this.viewEl).remove();
42948         }
42949          
42950         Roo.form.DayPicker.superclass.onDestroy.call(this);
42951     }
42952
42953 });//<script type="text/javasscript">
42954  
42955
42956 /**
42957  * @class Roo.DDView
42958  * A DnD enabled version of Roo.View.
42959  * @param {Element/String} container The Element in which to create the View.
42960  * @param {String} tpl The template string used to create the markup for each element of the View
42961  * @param {Object} config The configuration properties. These include all the config options of
42962  * {@link Roo.View} plus some specific to this class.<br>
42963  * <p>
42964  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
42965  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
42966  * <p>
42967  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
42968 .x-view-drag-insert-above {
42969         border-top:1px dotted #3366cc;
42970 }
42971 .x-view-drag-insert-below {
42972         border-bottom:1px dotted #3366cc;
42973 }
42974 </code></pre>
42975  * 
42976  */
42977  
42978 Roo.DDView = function(container, tpl, config) {
42979     Roo.DDView.superclass.constructor.apply(this, arguments);
42980     this.getEl().setStyle("outline", "0px none");
42981     this.getEl().unselectable();
42982     if (this.dragGroup) {
42983                 this.setDraggable(this.dragGroup.split(","));
42984     }
42985     if (this.dropGroup) {
42986                 this.setDroppable(this.dropGroup.split(","));
42987     }
42988     if (this.deletable) {
42989         this.setDeletable();
42990     }
42991     this.isDirtyFlag = false;
42992         this.addEvents({
42993                 "drop" : true
42994         });
42995 };
42996
42997 Roo.extend(Roo.DDView, Roo.View, {
42998 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
42999 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
43000 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
43001 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
43002
43003         isFormField: true,
43004
43005         reset: Roo.emptyFn,
43006         
43007         clearInvalid: Roo.form.Field.prototype.clearInvalid,
43008
43009         validate: function() {
43010                 return true;
43011         },
43012         
43013         destroy: function() {
43014                 this.purgeListeners();
43015                 this.getEl.removeAllListeners();
43016                 this.getEl().remove();
43017                 if (this.dragZone) {
43018                         if (this.dragZone.destroy) {
43019                                 this.dragZone.destroy();
43020                         }
43021                 }
43022                 if (this.dropZone) {
43023                         if (this.dropZone.destroy) {
43024                                 this.dropZone.destroy();
43025                         }
43026                 }
43027         },
43028
43029 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
43030         getName: function() {
43031                 return this.name;
43032         },
43033
43034 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43035         setValue: function(v) {
43036                 if (!this.store) {
43037                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43038                 }
43039                 var data = {};
43040                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43041                 this.store.proxy = new Roo.data.MemoryProxy(data);
43042                 this.store.load();
43043         },
43044
43045 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43046         getValue: function() {
43047                 var result = '(';
43048                 this.store.each(function(rec) {
43049                         result += rec.id + ',';
43050                 });
43051                 return result.substr(0, result.length - 1) + ')';
43052         },
43053         
43054         getIds: function() {
43055                 var i = 0, result = new Array(this.store.getCount());
43056                 this.store.each(function(rec) {
43057                         result[i++] = rec.id;
43058                 });
43059                 return result;
43060         },
43061         
43062         isDirty: function() {
43063                 return this.isDirtyFlag;
43064         },
43065
43066 /**
43067  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43068  *      whole Element becomes the target, and this causes the drop gesture to append.
43069  */
43070     getTargetFromEvent : function(e) {
43071                 var target = e.getTarget();
43072                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43073                 target = target.parentNode;
43074                 }
43075                 if (!target) {
43076                         target = this.el.dom.lastChild || this.el.dom;
43077                 }
43078                 return target;
43079     },
43080
43081 /**
43082  *      Create the drag data which consists of an object which has the property "ddel" as
43083  *      the drag proxy element. 
43084  */
43085     getDragData : function(e) {
43086         var target = this.findItemFromChild(e.getTarget());
43087                 if(target) {
43088                         this.handleSelection(e);
43089                         var selNodes = this.getSelectedNodes();
43090             var dragData = {
43091                 source: this,
43092                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43093                 nodes: selNodes,
43094                 records: []
43095                         };
43096                         var selectedIndices = this.getSelectedIndexes();
43097                         for (var i = 0; i < selectedIndices.length; i++) {
43098                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43099                         }
43100                         if (selNodes.length == 1) {
43101                                 dragData.ddel = target.cloneNode(true); // the div element
43102                         } else {
43103                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43104                                 div.className = 'multi-proxy';
43105                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43106                                         div.appendChild(selNodes[i].cloneNode(true));
43107                                 }
43108                                 dragData.ddel = div;
43109                         }
43110             //console.log(dragData)
43111             //console.log(dragData.ddel.innerHTML)
43112                         return dragData;
43113                 }
43114         //console.log('nodragData')
43115                 return false;
43116     },
43117     
43118 /**     Specify to which ddGroup items in this DDView may be dragged. */
43119     setDraggable: function(ddGroup) {
43120         if (ddGroup instanceof Array) {
43121                 Roo.each(ddGroup, this.setDraggable, this);
43122                 return;
43123         }
43124         if (this.dragZone) {
43125                 this.dragZone.addToGroup(ddGroup);
43126         } else {
43127                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
43128                                 containerScroll: true,
43129                                 ddGroup: ddGroup 
43130
43131                         });
43132 //                      Draggability implies selection. DragZone's mousedown selects the element.
43133                         if (!this.multiSelect) { this.singleSelect = true; }
43134
43135 //                      Wire the DragZone's handlers up to methods in *this*
43136                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
43137                 }
43138     },
43139
43140 /**     Specify from which ddGroup this DDView accepts drops. */
43141     setDroppable: function(ddGroup) {
43142         if (ddGroup instanceof Array) {
43143                 Roo.each(ddGroup, this.setDroppable, this);
43144                 return;
43145         }
43146         if (this.dropZone) {
43147                 this.dropZone.addToGroup(ddGroup);
43148         } else {
43149                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
43150                                 containerScroll: true,
43151                                 ddGroup: ddGroup
43152                         });
43153
43154 //                      Wire the DropZone's handlers up to methods in *this*
43155                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
43156                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
43157                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
43158                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
43159                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
43160                 }
43161     },
43162
43163 /**     Decide whether to drop above or below a View node. */
43164     getDropPoint : function(e, n, dd){
43165         if (n == this.el.dom) { return "above"; }
43166                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
43167                 var c = t + (b - t) / 2;
43168                 var y = Roo.lib.Event.getPageY(e);
43169                 if(y <= c) {
43170                         return "above";
43171                 }else{
43172                         return "below";
43173                 }
43174     },
43175
43176     onNodeEnter : function(n, dd, e, data){
43177                 return false;
43178     },
43179     
43180     onNodeOver : function(n, dd, e, data){
43181                 var pt = this.getDropPoint(e, n, dd);
43182                 // set the insert point style on the target node
43183                 var dragElClass = this.dropNotAllowed;
43184                 if (pt) {
43185                         var targetElClass;
43186                         if (pt == "above"){
43187                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
43188                                 targetElClass = "x-view-drag-insert-above";
43189                         } else {
43190                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
43191                                 targetElClass = "x-view-drag-insert-below";
43192                         }
43193                         if (this.lastInsertClass != targetElClass){
43194                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
43195                                 this.lastInsertClass = targetElClass;
43196                         }
43197                 }
43198                 return dragElClass;
43199         },
43200
43201     onNodeOut : function(n, dd, e, data){
43202                 this.removeDropIndicators(n);
43203     },
43204
43205     onNodeDrop : function(n, dd, e, data){
43206         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
43207                 return false;
43208         }
43209         var pt = this.getDropPoint(e, n, dd);
43210                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
43211                 if (pt == "below") { insertAt++; }
43212                 for (var i = 0; i < data.records.length; i++) {
43213                         var r = data.records[i];
43214                         var dup = this.store.getById(r.id);
43215                         if (dup && (dd != this.dragZone)) {
43216                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
43217                         } else {
43218                                 if (data.copy) {
43219                                         this.store.insert(insertAt++, r.copy());
43220                                 } else {
43221                                         data.source.isDirtyFlag = true;
43222                                         r.store.remove(r);
43223                                         this.store.insert(insertAt++, r);
43224                                 }
43225                                 this.isDirtyFlag = true;
43226                         }
43227                 }
43228                 this.dragZone.cachedTarget = null;
43229                 return true;
43230     },
43231
43232     removeDropIndicators : function(n){
43233                 if(n){
43234                         Roo.fly(n).removeClass([
43235                                 "x-view-drag-insert-above",
43236                                 "x-view-drag-insert-below"]);
43237                         this.lastInsertClass = "_noclass";
43238                 }
43239     },
43240
43241 /**
43242  *      Utility method. Add a delete option to the DDView's context menu.
43243  *      @param {String} imageUrl The URL of the "delete" icon image.
43244  */
43245         setDeletable: function(imageUrl) {
43246                 if (!this.singleSelect && !this.multiSelect) {
43247                         this.singleSelect = true;
43248                 }
43249                 var c = this.getContextMenu();
43250                 this.contextMenu.on("itemclick", function(item) {
43251                         switch (item.id) {
43252                                 case "delete":
43253                                         this.remove(this.getSelectedIndexes());
43254                                         break;
43255                         }
43256                 }, this);
43257                 this.contextMenu.add({
43258                         icon: imageUrl,
43259                         id: "delete",
43260                         text: 'Delete'
43261                 });
43262         },
43263         
43264 /**     Return the context menu for this DDView. */
43265         getContextMenu: function() {
43266                 if (!this.contextMenu) {
43267 //                      Create the View's context menu
43268                         this.contextMenu = new Roo.menu.Menu({
43269                                 id: this.id + "-contextmenu"
43270                         });
43271                         this.el.on("contextmenu", this.showContextMenu, this);
43272                 }
43273                 return this.contextMenu;
43274         },
43275         
43276         disableContextMenu: function() {
43277                 if (this.contextMenu) {
43278                         this.el.un("contextmenu", this.showContextMenu, this);
43279                 }
43280         },
43281
43282         showContextMenu: function(e, item) {
43283         item = this.findItemFromChild(e.getTarget());
43284                 if (item) {
43285                         e.stopEvent();
43286                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
43287                         this.contextMenu.showAt(e.getXY());
43288             }
43289     },
43290
43291 /**
43292  *      Remove {@link Roo.data.Record}s at the specified indices.
43293  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
43294  */
43295     remove: function(selectedIndices) {
43296                 selectedIndices = [].concat(selectedIndices);
43297                 for (var i = 0; i < selectedIndices.length; i++) {
43298                         var rec = this.store.getAt(selectedIndices[i]);
43299                         this.store.remove(rec);
43300                 }
43301     },
43302
43303 /**
43304  *      Double click fires the event, but also, if this is draggable, and there is only one other
43305  *      related DropZone, it transfers the selected node.
43306  */
43307     onDblClick : function(e){
43308         var item = this.findItemFromChild(e.getTarget());
43309         if(item){
43310             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
43311                 return false;
43312             }
43313             if (this.dragGroup) {
43314                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
43315                     while (targets.indexOf(this.dropZone) > -1) {
43316                             targets.remove(this.dropZone);
43317                                 }
43318                     if (targets.length == 1) {
43319                                         this.dragZone.cachedTarget = null;
43320                         var el = Roo.get(targets[0].getEl());
43321                         var box = el.getBox(true);
43322                         targets[0].onNodeDrop(el.dom, {
43323                                 target: el.dom,
43324                                 xy: [box.x, box.y + box.height - 1]
43325                         }, null, this.getDragData(e));
43326                     }
43327                 }
43328         }
43329     },
43330     
43331     handleSelection: function(e) {
43332                 this.dragZone.cachedTarget = null;
43333         var item = this.findItemFromChild(e.getTarget());
43334         if (!item) {
43335                 this.clearSelections(true);
43336                 return;
43337         }
43338                 if (item && (this.multiSelect || this.singleSelect)){
43339                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
43340                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
43341                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
43342                                 this.unselect(item);
43343                         } else {
43344                                 this.select(item, this.multiSelect && e.ctrlKey);
43345                                 this.lastSelection = item;
43346                         }
43347                 }
43348     },
43349
43350     onItemClick : function(item, index, e){
43351                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
43352                         return false;
43353                 }
43354                 return true;
43355     },
43356
43357     unselect : function(nodeInfo, suppressEvent){
43358                 var node = this.getNode(nodeInfo);
43359                 if(node && this.isSelected(node)){
43360                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
43361                                 Roo.fly(node).removeClass(this.selectedClass);
43362                                 this.selections.remove(node);
43363                                 if(!suppressEvent){
43364                                         this.fireEvent("selectionchange", this, this.selections);
43365                                 }
43366                         }
43367                 }
43368     }
43369 });
43370 /*
43371  * Based on:
43372  * Ext JS Library 1.1.1
43373  * Copyright(c) 2006-2007, Ext JS, LLC.
43374  *
43375  * Originally Released Under LGPL - original licence link has changed is not relivant.
43376  *
43377  * Fork - LGPL
43378  * <script type="text/javascript">
43379  */
43380  
43381 /**
43382  * @class Roo.LayoutManager
43383  * @extends Roo.util.Observable
43384  * Base class for layout managers.
43385  */
43386 Roo.LayoutManager = function(container, config){
43387     Roo.LayoutManager.superclass.constructor.call(this);
43388     this.el = Roo.get(container);
43389     // ie scrollbar fix
43390     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
43391         document.body.scroll = "no";
43392     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
43393         this.el.position('relative');
43394     }
43395     this.id = this.el.id;
43396     this.el.addClass("x-layout-container");
43397     /** false to disable window resize monitoring @type Boolean */
43398     this.monitorWindowResize = true;
43399     this.regions = {};
43400     this.addEvents({
43401         /**
43402          * @event layout
43403          * Fires when a layout is performed. 
43404          * @param {Roo.LayoutManager} this
43405          */
43406         "layout" : true,
43407         /**
43408          * @event regionresized
43409          * Fires when the user resizes a region. 
43410          * @param {Roo.LayoutRegion} region The resized region
43411          * @param {Number} newSize The new size (width for east/west, height for north/south)
43412          */
43413         "regionresized" : true,
43414         /**
43415          * @event regioncollapsed
43416          * Fires when a region is collapsed. 
43417          * @param {Roo.LayoutRegion} region The collapsed region
43418          */
43419         "regioncollapsed" : true,
43420         /**
43421          * @event regionexpanded
43422          * Fires when a region is expanded.  
43423          * @param {Roo.LayoutRegion} region The expanded region
43424          */
43425         "regionexpanded" : true
43426     });
43427     this.updating = false;
43428     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
43429 };
43430
43431 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
43432     /**
43433      * Returns true if this layout is currently being updated
43434      * @return {Boolean}
43435      */
43436     isUpdating : function(){
43437         return this.updating; 
43438     },
43439     
43440     /**
43441      * Suspend the LayoutManager from doing auto-layouts while
43442      * making multiple add or remove calls
43443      */
43444     beginUpdate : function(){
43445         this.updating = true;    
43446     },
43447     
43448     /**
43449      * Restore auto-layouts and optionally disable the manager from performing a layout
43450      * @param {Boolean} noLayout true to disable a layout update 
43451      */
43452     endUpdate : function(noLayout){
43453         this.updating = false;
43454         if(!noLayout){
43455             this.layout();
43456         }    
43457     },
43458     
43459     layout: function(){
43460         
43461     },
43462     
43463     onRegionResized : function(region, newSize){
43464         this.fireEvent("regionresized", region, newSize);
43465         this.layout();
43466     },
43467     
43468     onRegionCollapsed : function(region){
43469         this.fireEvent("regioncollapsed", region);
43470     },
43471     
43472     onRegionExpanded : function(region){
43473         this.fireEvent("regionexpanded", region);
43474     },
43475         
43476     /**
43477      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
43478      * performs box-model adjustments.
43479      * @return {Object} The size as an object {width: (the width), height: (the height)}
43480      */
43481     getViewSize : function(){
43482         var size;
43483         if(this.el.dom != document.body){
43484             size = this.el.getSize();
43485         }else{
43486             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43487         }
43488         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43489         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43490         return size;
43491     },
43492     
43493     /**
43494      * Returns the Element this layout is bound to.
43495      * @return {Roo.Element}
43496      */
43497     getEl : function(){
43498         return this.el;
43499     },
43500     
43501     /**
43502      * Returns the specified region.
43503      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43504      * @return {Roo.LayoutRegion}
43505      */
43506     getRegion : function(target){
43507         return this.regions[target.toLowerCase()];
43508     },
43509     
43510     onWindowResize : function(){
43511         if(this.monitorWindowResize){
43512             this.layout();
43513         }
43514     }
43515 });/*
43516  * Based on:
43517  * Ext JS Library 1.1.1
43518  * Copyright(c) 2006-2007, Ext JS, LLC.
43519  *
43520  * Originally Released Under LGPL - original licence link has changed is not relivant.
43521  *
43522  * Fork - LGPL
43523  * <script type="text/javascript">
43524  */
43525 /**
43526  * @class Roo.BorderLayout
43527  * @extends Roo.LayoutManager
43528  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43529  * please see: <br><br>
43530  * <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>
43531  * <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>
43532  * Example:
43533  <pre><code>
43534  var layout = new Roo.BorderLayout(document.body, {
43535     north: {
43536         initialSize: 25,
43537         titlebar: false
43538     },
43539     west: {
43540         split:true,
43541         initialSize: 200,
43542         minSize: 175,
43543         maxSize: 400,
43544         titlebar: true,
43545         collapsible: true
43546     },
43547     east: {
43548         split:true,
43549         initialSize: 202,
43550         minSize: 175,
43551         maxSize: 400,
43552         titlebar: true,
43553         collapsible: true
43554     },
43555     south: {
43556         split:true,
43557         initialSize: 100,
43558         minSize: 100,
43559         maxSize: 200,
43560         titlebar: true,
43561         collapsible: true
43562     },
43563     center: {
43564         titlebar: true,
43565         autoScroll:true,
43566         resizeTabs: true,
43567         minTabWidth: 50,
43568         preferredTabWidth: 150
43569     }
43570 });
43571
43572 // shorthand
43573 var CP = Roo.ContentPanel;
43574
43575 layout.beginUpdate();
43576 layout.add("north", new CP("north", "North"));
43577 layout.add("south", new CP("south", {title: "South", closable: true}));
43578 layout.add("west", new CP("west", {title: "West"}));
43579 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
43580 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
43581 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
43582 layout.getRegion("center").showPanel("center1");
43583 layout.endUpdate();
43584 </code></pre>
43585
43586 <b>The container the layout is rendered into can be either the body element or any other element.
43587 If it is not the body element, the container needs to either be an absolute positioned element,
43588 or you will need to add "position:relative" to the css of the container.  You will also need to specify
43589 the container size if it is not the body element.</b>
43590
43591 * @constructor
43592 * Create a new BorderLayout
43593 * @param {String/HTMLElement/Element} container The container this layout is bound to
43594 * @param {Object} config Configuration options
43595  */
43596 Roo.BorderLayout = function(container, config){
43597     config = config || {};
43598     Roo.BorderLayout.superclass.constructor.call(this, container, config);
43599     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
43600     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
43601         var target = this.factory.validRegions[i];
43602         if(config[target]){
43603             this.addRegion(target, config[target]);
43604         }
43605     }
43606 };
43607
43608 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
43609     /**
43610      * Creates and adds a new region if it doesn't already exist.
43611      * @param {String} target The target region key (north, south, east, west or center).
43612      * @param {Object} config The regions config object
43613      * @return {BorderLayoutRegion} The new region
43614      */
43615     addRegion : function(target, config){
43616         if(!this.regions[target]){
43617             var r = this.factory.create(target, this, config);
43618             this.bindRegion(target, r);
43619         }
43620         return this.regions[target];
43621     },
43622
43623     // private (kinda)
43624     bindRegion : function(name, r){
43625         this.regions[name] = r;
43626         r.on("visibilitychange", this.layout, this);
43627         r.on("paneladded", this.layout, this);
43628         r.on("panelremoved", this.layout, this);
43629         r.on("invalidated", this.layout, this);
43630         r.on("resized", this.onRegionResized, this);
43631         r.on("collapsed", this.onRegionCollapsed, this);
43632         r.on("expanded", this.onRegionExpanded, this);
43633     },
43634
43635     /**
43636      * Performs a layout update.
43637      */
43638     layout : function(){
43639         if(this.updating) return;
43640         var size = this.getViewSize();
43641         var w = size.width;
43642         var h = size.height;
43643         var centerW = w;
43644         var centerH = h;
43645         var centerY = 0;
43646         var centerX = 0;
43647         //var x = 0, y = 0;
43648
43649         var rs = this.regions;
43650         var north = rs["north"];
43651         var south = rs["south"]; 
43652         var west = rs["west"];
43653         var east = rs["east"];
43654         var center = rs["center"];
43655         //if(this.hideOnLayout){ // not supported anymore
43656             //c.el.setStyle("display", "none");
43657         //}
43658         if(north && north.isVisible()){
43659             var b = north.getBox();
43660             var m = north.getMargins();
43661             b.width = w - (m.left+m.right);
43662             b.x = m.left;
43663             b.y = m.top;
43664             centerY = b.height + b.y + m.bottom;
43665             centerH -= centerY;
43666             north.updateBox(this.safeBox(b));
43667         }
43668         if(south && south.isVisible()){
43669             var b = south.getBox();
43670             var m = south.getMargins();
43671             b.width = w - (m.left+m.right);
43672             b.x = m.left;
43673             var totalHeight = (b.height + m.top + m.bottom);
43674             b.y = h - totalHeight + m.top;
43675             centerH -= totalHeight;
43676             south.updateBox(this.safeBox(b));
43677         }
43678         if(west && west.isVisible()){
43679             var b = west.getBox();
43680             var m = west.getMargins();
43681             b.height = centerH - (m.top+m.bottom);
43682             b.x = m.left;
43683             b.y = centerY + m.top;
43684             var totalWidth = (b.width + m.left + m.right);
43685             centerX += totalWidth;
43686             centerW -= totalWidth;
43687             west.updateBox(this.safeBox(b));
43688         }
43689         if(east && east.isVisible()){
43690             var b = east.getBox();
43691             var m = east.getMargins();
43692             b.height = centerH - (m.top+m.bottom);
43693             var totalWidth = (b.width + m.left + m.right);
43694             b.x = w - totalWidth + m.left;
43695             b.y = centerY + m.top;
43696             centerW -= totalWidth;
43697             east.updateBox(this.safeBox(b));
43698         }
43699         if(center){
43700             var m = center.getMargins();
43701             var centerBox = {
43702                 x: centerX + m.left,
43703                 y: centerY + m.top,
43704                 width: centerW - (m.left+m.right),
43705                 height: centerH - (m.top+m.bottom)
43706             };
43707             //if(this.hideOnLayout){
43708                 //center.el.setStyle("display", "block");
43709             //}
43710             center.updateBox(this.safeBox(centerBox));
43711         }
43712         this.el.repaint();
43713         this.fireEvent("layout", this);
43714     },
43715
43716     // private
43717     safeBox : function(box){
43718         box.width = Math.max(0, box.width);
43719         box.height = Math.max(0, box.height);
43720         return box;
43721     },
43722
43723     /**
43724      * Adds a ContentPanel (or subclass) to this layout.
43725      * @param {String} target The target region key (north, south, east, west or center).
43726      * @param {Roo.ContentPanel} panel The panel to add
43727      * @return {Roo.ContentPanel} The added panel
43728      */
43729     add : function(target, panel){
43730          
43731         target = target.toLowerCase();
43732         return this.regions[target].add(panel);
43733     },
43734
43735     /**
43736      * Remove a ContentPanel (or subclass) to this layout.
43737      * @param {String} target The target region key (north, south, east, west or center).
43738      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43739      * @return {Roo.ContentPanel} The removed panel
43740      */
43741     remove : function(target, panel){
43742         target = target.toLowerCase();
43743         return this.regions[target].remove(panel);
43744     },
43745
43746     /**
43747      * Searches all regions for a panel with the specified id
43748      * @param {String} panelId
43749      * @return {Roo.ContentPanel} The panel or null if it wasn't found
43750      */
43751     findPanel : function(panelId){
43752         var rs = this.regions;
43753         for(var target in rs){
43754             if(typeof rs[target] != "function"){
43755                 var p = rs[target].getPanel(panelId);
43756                 if(p){
43757                     return p;
43758                 }
43759             }
43760         }
43761         return null;
43762     },
43763
43764     /**
43765      * Searches all regions for a panel with the specified id and activates (shows) it.
43766      * @param {String/ContentPanel} panelId The panels id or the panel itself
43767      * @return {Roo.ContentPanel} The shown panel or null
43768      */
43769     showPanel : function(panelId) {
43770       var rs = this.regions;
43771       for(var target in rs){
43772          var r = rs[target];
43773          if(typeof r != "function"){
43774             if(r.hasPanel(panelId)){
43775                return r.showPanel(panelId);
43776             }
43777          }
43778       }
43779       return null;
43780    },
43781
43782    /**
43783      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43784      * @param {Roo.state.Provider} provider (optional) An alternate state provider
43785      */
43786     restoreState : function(provider){
43787         if(!provider){
43788             provider = Roo.state.Manager;
43789         }
43790         var sm = new Roo.LayoutStateManager();
43791         sm.init(this, provider);
43792     },
43793
43794     /**
43795      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
43796      * object should contain properties for each region to add ContentPanels to, and each property's value should be
43797      * a valid ContentPanel config object.  Example:
43798      * <pre><code>
43799 // Create the main layout
43800 var layout = new Roo.BorderLayout('main-ct', {
43801     west: {
43802         split:true,
43803         minSize: 175,
43804         titlebar: true
43805     },
43806     center: {
43807         title:'Components'
43808     }
43809 }, 'main-ct');
43810
43811 // Create and add multiple ContentPanels at once via configs
43812 layout.batchAdd({
43813    west: {
43814        id: 'source-files',
43815        autoCreate:true,
43816        title:'Ext Source Files',
43817        autoScroll:true,
43818        fitToFrame:true
43819    },
43820    center : {
43821        el: cview,
43822        autoScroll:true,
43823        fitToFrame:true,
43824        toolbar: tb,
43825        resizeEl:'cbody'
43826    }
43827 });
43828 </code></pre>
43829      * @param {Object} regions An object containing ContentPanel configs by region name
43830      */
43831     batchAdd : function(regions){
43832         this.beginUpdate();
43833         for(var rname in regions){
43834             var lr = this.regions[rname];
43835             if(lr){
43836                 this.addTypedPanels(lr, regions[rname]);
43837             }
43838         }
43839         this.endUpdate();
43840     },
43841
43842     // private
43843     addTypedPanels : function(lr, ps){
43844         if(typeof ps == 'string'){
43845             lr.add(new Roo.ContentPanel(ps));
43846         }
43847         else if(ps instanceof Array){
43848             for(var i =0, len = ps.length; i < len; i++){
43849                 this.addTypedPanels(lr, ps[i]);
43850             }
43851         }
43852         else if(!ps.events){ // raw config?
43853             var el = ps.el;
43854             delete ps.el; // prevent conflict
43855             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
43856         }
43857         else {  // panel object assumed!
43858             lr.add(ps);
43859         }
43860     },
43861     /**
43862      * Adds a xtype elements to the layout.
43863      * <pre><code>
43864
43865 layout.addxtype({
43866        xtype : 'ContentPanel',
43867        region: 'west',
43868        items: [ .... ]
43869    }
43870 );
43871
43872 layout.addxtype({
43873         xtype : 'NestedLayoutPanel',
43874         region: 'west',
43875         layout: {
43876            center: { },
43877            west: { }   
43878         },
43879         items : [ ... list of content panels or nested layout panels.. ]
43880    }
43881 );
43882 </code></pre>
43883      * @param {Object} cfg Xtype definition of item to add.
43884      */
43885     addxtype : function(cfg)
43886     {
43887         // basically accepts a pannel...
43888         // can accept a layout region..!?!?
43889        // console.log('BorderLayout add ' + cfg.xtype)
43890         
43891         if (!cfg.xtype.match(/Panel$/)) {
43892             return false;
43893         }
43894         var ret = false;
43895         var region = cfg.region;
43896         delete cfg.region;
43897         
43898           
43899         var xitems = [];
43900         if (cfg.items) {
43901             xitems = cfg.items;
43902             delete cfg.items;
43903         }
43904         
43905         
43906         switch(cfg.xtype) 
43907         {
43908             case 'ContentPanel':  // ContentPanel (el, cfg)
43909             case 'ScrollPanel':  // ContentPanel (el, cfg)
43910                 if(cfg.autoCreate) {
43911                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43912                 } else {
43913                     var el = this.el.createChild();
43914                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43915                 }
43916                 
43917                 this.add(region, ret);
43918                 break;
43919             
43920             
43921             case 'TreePanel': // our new panel!
43922                 cfg.el = this.el.createChild();
43923                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43924                 this.add(region, ret);
43925                 break;
43926             
43927             case 'NestedLayoutPanel': 
43928                 // create a new Layout (which is  a Border Layout...
43929                 var el = this.el.createChild();
43930                 var clayout = cfg.layout;
43931                 delete cfg.layout;
43932                 clayout.items   = clayout.items  || [];
43933                 // replace this exitems with the clayout ones..
43934                 xitems = clayout.items;
43935                  
43936                 
43937                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43938                     cfg.background = false;
43939                 }
43940                 var layout = new Roo.BorderLayout(el, clayout);
43941                 
43942                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
43943                 //console.log('adding nested layout panel '  + cfg.toSource());
43944                 this.add(region, ret);
43945                 
43946                 break;
43947                 
43948             case 'GridPanel': 
43949             
43950                 // needs grid and region
43951                 
43952                 //var el = this.getRegion(region).el.createChild();
43953                 var el = this.el.createChild();
43954                 // create the grid first...
43955                 
43956                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
43957                 delete cfg.grid;
43958                 if (region == 'center' && this.active ) {
43959                     cfg.background = false;
43960                 }
43961                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
43962                 
43963                 this.add(region, ret);
43964                 if (cfg.background) {
43965                     ret.on('activate', function(gp) {
43966                         if (!gp.grid.rendered) {
43967                             gp.grid.render();
43968                         }
43969                     });
43970                 } else {
43971                     grid.render();
43972                 }
43973                 break;
43974            
43975                
43976                 
43977                 
43978             default: 
43979                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
43980                 return null;
43981              // GridPanel (grid, cfg)
43982             
43983         }
43984         this.beginUpdate();
43985         // add children..
43986         Roo.each(xitems, function(i)  {
43987             ret.addxtype(i);
43988         });
43989         this.endUpdate();
43990         return ret;
43991         
43992     }
43993 });
43994
43995 /**
43996  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
43997  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
43998  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
43999  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
44000  * <pre><code>
44001 // shorthand
44002 var CP = Roo.ContentPanel;
44003
44004 var layout = Roo.BorderLayout.create({
44005     north: {
44006         initialSize: 25,
44007         titlebar: false,
44008         panels: [new CP("north", "North")]
44009     },
44010     west: {
44011         split:true,
44012         initialSize: 200,
44013         minSize: 175,
44014         maxSize: 400,
44015         titlebar: true,
44016         collapsible: true,
44017         panels: [new CP("west", {title: "West"})]
44018     },
44019     east: {
44020         split:true,
44021         initialSize: 202,
44022         minSize: 175,
44023         maxSize: 400,
44024         titlebar: true,
44025         collapsible: true,
44026         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
44027     },
44028     south: {
44029         split:true,
44030         initialSize: 100,
44031         minSize: 100,
44032         maxSize: 200,
44033         titlebar: true,
44034         collapsible: true,
44035         panels: [new CP("south", {title: "South", closable: true})]
44036     },
44037     center: {
44038         titlebar: true,
44039         autoScroll:true,
44040         resizeTabs: true,
44041         minTabWidth: 50,
44042         preferredTabWidth: 150,
44043         panels: [
44044             new CP("center1", {title: "Close Me", closable: true}),
44045             new CP("center2", {title: "Center Panel", closable: false})
44046         ]
44047     }
44048 }, document.body);
44049
44050 layout.getRegion("center").showPanel("center1");
44051 </code></pre>
44052  * @param config
44053  * @param targetEl
44054  */
44055 Roo.BorderLayout.create = function(config, targetEl){
44056     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44057     layout.beginUpdate();
44058     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44059     for(var j = 0, jlen = regions.length; j < jlen; j++){
44060         var lr = regions[j];
44061         if(layout.regions[lr] && config[lr].panels){
44062             var r = layout.regions[lr];
44063             var ps = config[lr].panels;
44064             layout.addTypedPanels(r, ps);
44065         }
44066     }
44067     layout.endUpdate();
44068     return layout;
44069 };
44070
44071 // private
44072 Roo.BorderLayout.RegionFactory = {
44073     // private
44074     validRegions : ["north","south","east","west","center"],
44075
44076     // private
44077     create : function(target, mgr, config){
44078         target = target.toLowerCase();
44079         if(config.lightweight || config.basic){
44080             return new Roo.BasicLayoutRegion(mgr, config, target);
44081         }
44082         switch(target){
44083             case "north":
44084                 return new Roo.NorthLayoutRegion(mgr, config);
44085             case "south":
44086                 return new Roo.SouthLayoutRegion(mgr, config);
44087             case "east":
44088                 return new Roo.EastLayoutRegion(mgr, config);
44089             case "west":
44090                 return new Roo.WestLayoutRegion(mgr, config);
44091             case "center":
44092                 return new Roo.CenterLayoutRegion(mgr, config);
44093         }
44094         throw 'Layout region "'+target+'" not supported.';
44095     }
44096 };/*
44097  * Based on:
44098  * Ext JS Library 1.1.1
44099  * Copyright(c) 2006-2007, Ext JS, LLC.
44100  *
44101  * Originally Released Under LGPL - original licence link has changed is not relivant.
44102  *
44103  * Fork - LGPL
44104  * <script type="text/javascript">
44105  */
44106  
44107 /**
44108  * @class Roo.BasicLayoutRegion
44109  * @extends Roo.util.Observable
44110  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
44111  * and does not have a titlebar, tabs or any other features. All it does is size and position 
44112  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
44113  */
44114 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
44115     this.mgr = mgr;
44116     this.position  = pos;
44117     this.events = {
44118         /**
44119          * @scope Roo.BasicLayoutRegion
44120          */
44121         
44122         /**
44123          * @event beforeremove
44124          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
44125          * @param {Roo.LayoutRegion} this
44126          * @param {Roo.ContentPanel} panel The panel
44127          * @param {Object} e The cancel event object
44128          */
44129         "beforeremove" : true,
44130         /**
44131          * @event invalidated
44132          * Fires when the layout for this region is changed.
44133          * @param {Roo.LayoutRegion} this
44134          */
44135         "invalidated" : true,
44136         /**
44137          * @event visibilitychange
44138          * Fires when this region is shown or hidden 
44139          * @param {Roo.LayoutRegion} this
44140          * @param {Boolean} visibility true or false
44141          */
44142         "visibilitychange" : true,
44143         /**
44144          * @event paneladded
44145          * Fires when a panel is added. 
44146          * @param {Roo.LayoutRegion} this
44147          * @param {Roo.ContentPanel} panel The panel
44148          */
44149         "paneladded" : true,
44150         /**
44151          * @event panelremoved
44152          * Fires when a panel is removed. 
44153          * @param {Roo.LayoutRegion} this
44154          * @param {Roo.ContentPanel} panel The panel
44155          */
44156         "panelremoved" : true,
44157         /**
44158          * @event collapsed
44159          * Fires when this region is collapsed.
44160          * @param {Roo.LayoutRegion} this
44161          */
44162         "collapsed" : true,
44163         /**
44164          * @event expanded
44165          * Fires when this region is expanded.
44166          * @param {Roo.LayoutRegion} this
44167          */
44168         "expanded" : true,
44169         /**
44170          * @event slideshow
44171          * Fires when this region is slid into view.
44172          * @param {Roo.LayoutRegion} this
44173          */
44174         "slideshow" : true,
44175         /**
44176          * @event slidehide
44177          * Fires when this region slides out of view. 
44178          * @param {Roo.LayoutRegion} this
44179          */
44180         "slidehide" : true,
44181         /**
44182          * @event panelactivated
44183          * Fires when a panel is activated. 
44184          * @param {Roo.LayoutRegion} this
44185          * @param {Roo.ContentPanel} panel The activated panel
44186          */
44187         "panelactivated" : true,
44188         /**
44189          * @event resized
44190          * Fires when the user resizes this region. 
44191          * @param {Roo.LayoutRegion} this
44192          * @param {Number} newSize The new size (width for east/west, height for north/south)
44193          */
44194         "resized" : true
44195     };
44196     /** A collection of panels in this region. @type Roo.util.MixedCollection */
44197     this.panels = new Roo.util.MixedCollection();
44198     this.panels.getKey = this.getPanelId.createDelegate(this);
44199     this.box = null;
44200     this.activePanel = null;
44201     // ensure listeners are added...
44202     
44203     if (config.listeners || config.events) {
44204         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
44205             listeners : config.listeners || {},
44206             events : config.events || {}
44207         });
44208     }
44209     
44210     if(skipConfig !== true){
44211         this.applyConfig(config);
44212     }
44213 };
44214
44215 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
44216     getPanelId : function(p){
44217         return p.getId();
44218     },
44219     
44220     applyConfig : function(config){
44221         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44222         this.config = config;
44223         
44224     },
44225     
44226     /**
44227      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
44228      * the width, for horizontal (north, south) the height.
44229      * @param {Number} newSize The new width or height
44230      */
44231     resizeTo : function(newSize){
44232         var el = this.el ? this.el :
44233                  (this.activePanel ? this.activePanel.getEl() : null);
44234         if(el){
44235             switch(this.position){
44236                 case "east":
44237                 case "west":
44238                     el.setWidth(newSize);
44239                     this.fireEvent("resized", this, newSize);
44240                 break;
44241                 case "north":
44242                 case "south":
44243                     el.setHeight(newSize);
44244                     this.fireEvent("resized", this, newSize);
44245                 break;                
44246             }
44247         }
44248     },
44249     
44250     getBox : function(){
44251         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
44252     },
44253     
44254     getMargins : function(){
44255         return this.margins;
44256     },
44257     
44258     updateBox : function(box){
44259         this.box = box;
44260         var el = this.activePanel.getEl();
44261         el.dom.style.left = box.x + "px";
44262         el.dom.style.top = box.y + "px";
44263         this.activePanel.setSize(box.width, box.height);
44264     },
44265     
44266     /**
44267      * Returns the container element for this region.
44268      * @return {Roo.Element}
44269      */
44270     getEl : function(){
44271         return this.activePanel;
44272     },
44273     
44274     /**
44275      * Returns true if this region is currently visible.
44276      * @return {Boolean}
44277      */
44278     isVisible : function(){
44279         return this.activePanel ? true : false;
44280     },
44281     
44282     setActivePanel : function(panel){
44283         panel = this.getPanel(panel);
44284         if(this.activePanel && this.activePanel != panel){
44285             this.activePanel.setActiveState(false);
44286             this.activePanel.getEl().setLeftTop(-10000,-10000);
44287         }
44288         this.activePanel = panel;
44289         panel.setActiveState(true);
44290         if(this.box){
44291             panel.setSize(this.box.width, this.box.height);
44292         }
44293         this.fireEvent("panelactivated", this, panel);
44294         this.fireEvent("invalidated");
44295     },
44296     
44297     /**
44298      * Show the specified panel.
44299      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
44300      * @return {Roo.ContentPanel} The shown panel or null
44301      */
44302     showPanel : function(panel){
44303         if(panel = this.getPanel(panel)){
44304             this.setActivePanel(panel);
44305         }
44306         return panel;
44307     },
44308     
44309     /**
44310      * Get the active panel for this region.
44311      * @return {Roo.ContentPanel} The active panel or null
44312      */
44313     getActivePanel : function(){
44314         return this.activePanel;
44315     },
44316     
44317     /**
44318      * Add the passed ContentPanel(s)
44319      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44320      * @return {Roo.ContentPanel} The panel added (if only one was added)
44321      */
44322     add : function(panel){
44323         if(arguments.length > 1){
44324             for(var i = 0, len = arguments.length; i < len; i++) {
44325                 this.add(arguments[i]);
44326             }
44327             return null;
44328         }
44329         if(this.hasPanel(panel)){
44330             this.showPanel(panel);
44331             return panel;
44332         }
44333         var el = panel.getEl();
44334         if(el.dom.parentNode != this.mgr.el.dom){
44335             this.mgr.el.dom.appendChild(el.dom);
44336         }
44337         if(panel.setRegion){
44338             panel.setRegion(this);
44339         }
44340         this.panels.add(panel);
44341         el.setStyle("position", "absolute");
44342         if(!panel.background){
44343             this.setActivePanel(panel);
44344             if(this.config.initialSize && this.panels.getCount()==1){
44345                 this.resizeTo(this.config.initialSize);
44346             }
44347         }
44348         this.fireEvent("paneladded", this, panel);
44349         return panel;
44350     },
44351     
44352     /**
44353      * Returns true if the panel is in this region.
44354      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44355      * @return {Boolean}
44356      */
44357     hasPanel : function(panel){
44358         if(typeof panel == "object"){ // must be panel obj
44359             panel = panel.getId();
44360         }
44361         return this.getPanel(panel) ? true : false;
44362     },
44363     
44364     /**
44365      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44366      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44367      * @param {Boolean} preservePanel Overrides the config preservePanel option
44368      * @return {Roo.ContentPanel} The panel that was removed
44369      */
44370     remove : function(panel, preservePanel){
44371         panel = this.getPanel(panel);
44372         if(!panel){
44373             return null;
44374         }
44375         var e = {};
44376         this.fireEvent("beforeremove", this, panel, e);
44377         if(e.cancel === true){
44378             return null;
44379         }
44380         var panelId = panel.getId();
44381         this.panels.removeKey(panelId);
44382         return panel;
44383     },
44384     
44385     /**
44386      * Returns the panel specified or null if it's not in this region.
44387      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44388      * @return {Roo.ContentPanel}
44389      */
44390     getPanel : function(id){
44391         if(typeof id == "object"){ // must be panel obj
44392             return id;
44393         }
44394         return this.panels.get(id);
44395     },
44396     
44397     /**
44398      * Returns this regions position (north/south/east/west/center).
44399      * @return {String} 
44400      */
44401     getPosition: function(){
44402         return this.position;    
44403     }
44404 });/*
44405  * Based on:
44406  * Ext JS Library 1.1.1
44407  * Copyright(c) 2006-2007, Ext JS, LLC.
44408  *
44409  * Originally Released Under LGPL - original licence link has changed is not relivant.
44410  *
44411  * Fork - LGPL
44412  * <script type="text/javascript">
44413  */
44414  
44415 /**
44416  * @class Roo.LayoutRegion
44417  * @extends Roo.BasicLayoutRegion
44418  * This class represents a region in a layout manager.
44419  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
44420  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
44421  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
44422  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
44423  * @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})
44424  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
44425  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
44426  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
44427  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
44428  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
44429  * @cfg {String}    title           The title for the region (overrides panel titles)
44430  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
44431  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
44432  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
44433  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
44434  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
44435  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
44436  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
44437  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
44438  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
44439  * @cfg {Boolean}   showPin         True to show a pin button
44440  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
44441  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
44442  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
44443  * @cfg {Number}    width           For East/West panels
44444  * @cfg {Number}    height          For North/South panels
44445  * @cfg {Boolean}   split           To show the splitter
44446  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
44447  */
44448 Roo.LayoutRegion = function(mgr, config, pos){
44449     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
44450     var dh = Roo.DomHelper;
44451     /** This region's container element 
44452     * @type Roo.Element */
44453     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
44454     /** This region's title element 
44455     * @type Roo.Element */
44456
44457     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
44458         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
44459         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
44460     ]}, true);
44461     this.titleEl.enableDisplayMode();
44462     /** This region's title text element 
44463     * @type HTMLElement */
44464     this.titleTextEl = this.titleEl.dom.firstChild;
44465     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
44466     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
44467     this.closeBtn.enableDisplayMode();
44468     this.closeBtn.on("click", this.closeClicked, this);
44469     this.closeBtn.hide();
44470
44471     this.createBody(config);
44472     this.visible = true;
44473     this.collapsed = false;
44474
44475     if(config.hideWhenEmpty){
44476         this.hide();
44477         this.on("paneladded", this.validateVisibility, this);
44478         this.on("panelremoved", this.validateVisibility, this);
44479     }
44480     this.applyConfig(config);
44481 };
44482
44483 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
44484
44485     createBody : function(){
44486         /** This region's body element 
44487         * @type Roo.Element */
44488         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
44489     },
44490
44491     applyConfig : function(c){
44492         if(c.collapsible && this.position != "center" && !this.collapsedEl){
44493             var dh = Roo.DomHelper;
44494             if(c.titlebar !== false){
44495                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
44496                 this.collapseBtn.on("click", this.collapse, this);
44497                 this.collapseBtn.enableDisplayMode();
44498
44499                 if(c.showPin === true || this.showPin){
44500                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
44501                     this.stickBtn.enableDisplayMode();
44502                     this.stickBtn.on("click", this.expand, this);
44503                     this.stickBtn.hide();
44504                 }
44505             }
44506             /** This region's collapsed element
44507             * @type Roo.Element */
44508             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44509                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44510             ]}, true);
44511             if(c.floatable !== false){
44512                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44513                this.collapsedEl.on("click", this.collapseClick, this);
44514             }
44515
44516             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44517                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44518                    id: "message", unselectable: "on", style:{"float":"left"}});
44519                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44520              }
44521             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44522             this.expandBtn.on("click", this.expand, this);
44523         }
44524         if(this.collapseBtn){
44525             this.collapseBtn.setVisible(c.collapsible == true);
44526         }
44527         this.cmargins = c.cmargins || this.cmargins ||
44528                          (this.position == "west" || this.position == "east" ?
44529                              {top: 0, left: 2, right:2, bottom: 0} :
44530                              {top: 2, left: 0, right:0, bottom: 2});
44531         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44532         this.bottomTabs = c.tabPosition != "top";
44533         this.autoScroll = c.autoScroll || false;
44534         if(this.autoScroll){
44535             this.bodyEl.setStyle("overflow", "auto");
44536         }else{
44537             this.bodyEl.setStyle("overflow", "hidden");
44538         }
44539         //if(c.titlebar !== false){
44540             if((!c.titlebar && !c.title) || c.titlebar === false){
44541                 this.titleEl.hide();
44542             }else{
44543                 this.titleEl.show();
44544                 if(c.title){
44545                     this.titleTextEl.innerHTML = c.title;
44546                 }
44547             }
44548         //}
44549         this.duration = c.duration || .30;
44550         this.slideDuration = c.slideDuration || .45;
44551         this.config = c;
44552         if(c.collapsed){
44553             this.collapse(true);
44554         }
44555         if(c.hidden){
44556             this.hide();
44557         }
44558     },
44559     /**
44560      * Returns true if this region is currently visible.
44561      * @return {Boolean}
44562      */
44563     isVisible : function(){
44564         return this.visible;
44565     },
44566
44567     /**
44568      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44569      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
44570      */
44571     setCollapsedTitle : function(title){
44572         title = title || "&#160;";
44573         if(this.collapsedTitleTextEl){
44574             this.collapsedTitleTextEl.innerHTML = title;
44575         }
44576     },
44577
44578     getBox : function(){
44579         var b;
44580         if(!this.collapsed){
44581             b = this.el.getBox(false, true);
44582         }else{
44583             b = this.collapsedEl.getBox(false, true);
44584         }
44585         return b;
44586     },
44587
44588     getMargins : function(){
44589         return this.collapsed ? this.cmargins : this.margins;
44590     },
44591
44592     highlight : function(){
44593         this.el.addClass("x-layout-panel-dragover");
44594     },
44595
44596     unhighlight : function(){
44597         this.el.removeClass("x-layout-panel-dragover");
44598     },
44599
44600     updateBox : function(box){
44601         this.box = box;
44602         if(!this.collapsed){
44603             this.el.dom.style.left = box.x + "px";
44604             this.el.dom.style.top = box.y + "px";
44605             this.updateBody(box.width, box.height);
44606         }else{
44607             this.collapsedEl.dom.style.left = box.x + "px";
44608             this.collapsedEl.dom.style.top = box.y + "px";
44609             this.collapsedEl.setSize(box.width, box.height);
44610         }
44611         if(this.tabs){
44612             this.tabs.autoSizeTabs();
44613         }
44614     },
44615
44616     updateBody : function(w, h){
44617         if(w !== null){
44618             this.el.setWidth(w);
44619             w -= this.el.getBorderWidth("rl");
44620             if(this.config.adjustments){
44621                 w += this.config.adjustments[0];
44622             }
44623         }
44624         if(h !== null){
44625             this.el.setHeight(h);
44626             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44627             h -= this.el.getBorderWidth("tb");
44628             if(this.config.adjustments){
44629                 h += this.config.adjustments[1];
44630             }
44631             this.bodyEl.setHeight(h);
44632             if(this.tabs){
44633                 h = this.tabs.syncHeight(h);
44634             }
44635         }
44636         if(this.panelSize){
44637             w = w !== null ? w : this.panelSize.width;
44638             h = h !== null ? h : this.panelSize.height;
44639         }
44640         if(this.activePanel){
44641             var el = this.activePanel.getEl();
44642             w = w !== null ? w : el.getWidth();
44643             h = h !== null ? h : el.getHeight();
44644             this.panelSize = {width: w, height: h};
44645             this.activePanel.setSize(w, h);
44646         }
44647         if(Roo.isIE && this.tabs){
44648             this.tabs.el.repaint();
44649         }
44650     },
44651
44652     /**
44653      * Returns the container element for this region.
44654      * @return {Roo.Element}
44655      */
44656     getEl : function(){
44657         return this.el;
44658     },
44659
44660     /**
44661      * Hides this region.
44662      */
44663     hide : function(){
44664         if(!this.collapsed){
44665             this.el.dom.style.left = "-2000px";
44666             this.el.hide();
44667         }else{
44668             this.collapsedEl.dom.style.left = "-2000px";
44669             this.collapsedEl.hide();
44670         }
44671         this.visible = false;
44672         this.fireEvent("visibilitychange", this, false);
44673     },
44674
44675     /**
44676      * Shows this region if it was previously hidden.
44677      */
44678     show : function(){
44679         if(!this.collapsed){
44680             this.el.show();
44681         }else{
44682             this.collapsedEl.show();
44683         }
44684         this.visible = true;
44685         this.fireEvent("visibilitychange", this, true);
44686     },
44687
44688     closeClicked : function(){
44689         if(this.activePanel){
44690             this.remove(this.activePanel);
44691         }
44692     },
44693
44694     collapseClick : function(e){
44695         if(this.isSlid){
44696            e.stopPropagation();
44697            this.slideIn();
44698         }else{
44699            e.stopPropagation();
44700            this.slideOut();
44701         }
44702     },
44703
44704     /**
44705      * Collapses this region.
44706      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44707      */
44708     collapse : function(skipAnim){
44709         if(this.collapsed) return;
44710         this.collapsed = true;
44711         if(this.split){
44712             this.split.el.hide();
44713         }
44714         if(this.config.animate && skipAnim !== true){
44715             this.fireEvent("invalidated", this);
44716             this.animateCollapse();
44717         }else{
44718             this.el.setLocation(-20000,-20000);
44719             this.el.hide();
44720             this.collapsedEl.show();
44721             this.fireEvent("collapsed", this);
44722             this.fireEvent("invalidated", this);
44723         }
44724     },
44725
44726     animateCollapse : function(){
44727         // overridden
44728     },
44729
44730     /**
44731      * Expands this region if it was previously collapsed.
44732      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44733      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44734      */
44735     expand : function(e, skipAnim){
44736         if(e) e.stopPropagation();
44737         if(!this.collapsed || this.el.hasActiveFx()) return;
44738         if(this.isSlid){
44739             this.afterSlideIn();
44740             skipAnim = true;
44741         }
44742         this.collapsed = false;
44743         if(this.config.animate && skipAnim !== true){
44744             this.animateExpand();
44745         }else{
44746             this.el.show();
44747             if(this.split){
44748                 this.split.el.show();
44749             }
44750             this.collapsedEl.setLocation(-2000,-2000);
44751             this.collapsedEl.hide();
44752             this.fireEvent("invalidated", this);
44753             this.fireEvent("expanded", this);
44754         }
44755     },
44756
44757     animateExpand : function(){
44758         // overridden
44759     },
44760
44761     initTabs : function()
44762     {
44763         this.bodyEl.setStyle("overflow", "hidden");
44764         var ts = new Roo.TabPanel(
44765                 this.bodyEl.dom,
44766                 {
44767                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
44768                     disableTooltips: this.config.disableTabTips,
44769                     toolbar : this.config.toolbar
44770                 }
44771         );
44772         if(this.config.hideTabs){
44773             ts.stripWrap.setDisplayed(false);
44774         }
44775         this.tabs = ts;
44776         ts.resizeTabs = this.config.resizeTabs === true;
44777         ts.minTabWidth = this.config.minTabWidth || 40;
44778         ts.maxTabWidth = this.config.maxTabWidth || 250;
44779         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44780         ts.monitorResize = false;
44781         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44782         ts.bodyEl.addClass('x-layout-tabs-body');
44783         this.panels.each(this.initPanelAsTab, this);
44784     },
44785
44786     initPanelAsTab : function(panel){
44787         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
44788                     this.config.closeOnTab && panel.isClosable());
44789         if(panel.tabTip !== undefined){
44790             ti.setTooltip(panel.tabTip);
44791         }
44792         ti.on("activate", function(){
44793               this.setActivePanel(panel);
44794         }, this);
44795         if(this.config.closeOnTab){
44796             ti.on("beforeclose", function(t, e){
44797                 e.cancel = true;
44798                 this.remove(panel);
44799             }, this);
44800         }
44801         return ti;
44802     },
44803
44804     updatePanelTitle : function(panel, title){
44805         if(this.activePanel == panel){
44806             this.updateTitle(title);
44807         }
44808         if(this.tabs){
44809             var ti = this.tabs.getTab(panel.getEl().id);
44810             ti.setText(title);
44811             if(panel.tabTip !== undefined){
44812                 ti.setTooltip(panel.tabTip);
44813             }
44814         }
44815     },
44816
44817     updateTitle : function(title){
44818         if(this.titleTextEl && !this.config.title){
44819             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44820         }
44821     },
44822
44823     setActivePanel : function(panel){
44824         panel = this.getPanel(panel);
44825         if(this.activePanel && this.activePanel != panel){
44826             this.activePanel.setActiveState(false);
44827         }
44828         this.activePanel = panel;
44829         panel.setActiveState(true);
44830         if(this.panelSize){
44831             panel.setSize(this.panelSize.width, this.panelSize.height);
44832         }
44833         if(this.closeBtn){
44834             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44835         }
44836         this.updateTitle(panel.getTitle());
44837         if(this.tabs){
44838             this.fireEvent("invalidated", this);
44839         }
44840         this.fireEvent("panelactivated", this, panel);
44841     },
44842
44843     /**
44844      * Shows the specified panel.
44845      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44846      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44847      */
44848     showPanel : function(panel){
44849         if(panel = this.getPanel(panel)){
44850             if(this.tabs){
44851                 var tab = this.tabs.getTab(panel.getEl().id);
44852                 if(tab.isHidden()){
44853                     this.tabs.unhideTab(tab.id);
44854                 }
44855                 tab.activate();
44856             }else{
44857                 this.setActivePanel(panel);
44858             }
44859         }
44860         return panel;
44861     },
44862
44863     /**
44864      * Get the active panel for this region.
44865      * @return {Roo.ContentPanel} The active panel or null
44866      */
44867     getActivePanel : function(){
44868         return this.activePanel;
44869     },
44870
44871     validateVisibility : function(){
44872         if(this.panels.getCount() < 1){
44873             this.updateTitle("&#160;");
44874             this.closeBtn.hide();
44875             this.hide();
44876         }else{
44877             if(!this.isVisible()){
44878                 this.show();
44879             }
44880         }
44881     },
44882
44883     /**
44884      * Adds the passed ContentPanel(s) to this region.
44885      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44886      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44887      */
44888     add : function(panel){
44889         if(arguments.length > 1){
44890             for(var i = 0, len = arguments.length; i < len; i++) {
44891                 this.add(arguments[i]);
44892             }
44893             return null;
44894         }
44895         if(this.hasPanel(panel)){
44896             this.showPanel(panel);
44897             return panel;
44898         }
44899         panel.setRegion(this);
44900         this.panels.add(panel);
44901         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44902             this.bodyEl.dom.appendChild(panel.getEl().dom);
44903             if(panel.background !== true){
44904                 this.setActivePanel(panel);
44905             }
44906             this.fireEvent("paneladded", this, panel);
44907             return panel;
44908         }
44909         if(!this.tabs){
44910             this.initTabs();
44911         }else{
44912             this.initPanelAsTab(panel);
44913         }
44914         if(panel.background !== true){
44915             this.tabs.activate(panel.getEl().id);
44916         }
44917         this.fireEvent("paneladded", this, panel);
44918         return panel;
44919     },
44920
44921     /**
44922      * Hides the tab for the specified panel.
44923      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44924      */
44925     hidePanel : function(panel){
44926         if(this.tabs && (panel = this.getPanel(panel))){
44927             this.tabs.hideTab(panel.getEl().id);
44928         }
44929     },
44930
44931     /**
44932      * Unhides the tab for a previously hidden panel.
44933      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44934      */
44935     unhidePanel : function(panel){
44936         if(this.tabs && (panel = this.getPanel(panel))){
44937             this.tabs.unhideTab(panel.getEl().id);
44938         }
44939     },
44940
44941     clearPanels : function(){
44942         while(this.panels.getCount() > 0){
44943              this.remove(this.panels.first());
44944         }
44945     },
44946
44947     /**
44948      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44949      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44950      * @param {Boolean} preservePanel Overrides the config preservePanel option
44951      * @return {Roo.ContentPanel} The panel that was removed
44952      */
44953     remove : function(panel, preservePanel){
44954         panel = this.getPanel(panel);
44955         if(!panel){
44956             return null;
44957         }
44958         var e = {};
44959         this.fireEvent("beforeremove", this, panel, e);
44960         if(e.cancel === true){
44961             return null;
44962         }
44963         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44964         var panelId = panel.getId();
44965         this.panels.removeKey(panelId);
44966         if(preservePanel){
44967             document.body.appendChild(panel.getEl().dom);
44968         }
44969         if(this.tabs){
44970             this.tabs.removeTab(panel.getEl().id);
44971         }else if (!preservePanel){
44972             this.bodyEl.dom.removeChild(panel.getEl().dom);
44973         }
44974         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44975             var p = this.panels.first();
44976             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44977             tempEl.appendChild(p.getEl().dom);
44978             this.bodyEl.update("");
44979             this.bodyEl.dom.appendChild(p.getEl().dom);
44980             tempEl = null;
44981             this.updateTitle(p.getTitle());
44982             this.tabs = null;
44983             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44984             this.setActivePanel(p);
44985         }
44986         panel.setRegion(null);
44987         if(this.activePanel == panel){
44988             this.activePanel = null;
44989         }
44990         if(this.config.autoDestroy !== false && preservePanel !== true){
44991             try{panel.destroy();}catch(e){}
44992         }
44993         this.fireEvent("panelremoved", this, panel);
44994         return panel;
44995     },
44996
44997     /**
44998      * Returns the TabPanel component used by this region
44999      * @return {Roo.TabPanel}
45000      */
45001     getTabs : function(){
45002         return this.tabs;
45003     },
45004
45005     createTool : function(parentEl, className){
45006         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
45007             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
45008         btn.addClassOnOver("x-layout-tools-button-over");
45009         return btn;
45010     }
45011 });/*
45012  * Based on:
45013  * Ext JS Library 1.1.1
45014  * Copyright(c) 2006-2007, Ext JS, LLC.
45015  *
45016  * Originally Released Under LGPL - original licence link has changed is not relivant.
45017  *
45018  * Fork - LGPL
45019  * <script type="text/javascript">
45020  */
45021  
45022
45023
45024 /**
45025  * @class Roo.SplitLayoutRegion
45026  * @extends Roo.LayoutRegion
45027  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
45028  */
45029 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
45030     this.cursor = cursor;
45031     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
45032 };
45033
45034 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
45035     splitTip : "Drag to resize.",
45036     collapsibleSplitTip : "Drag to resize. Double click to hide.",
45037     useSplitTips : false,
45038
45039     applyConfig : function(config){
45040         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
45041         if(config.split){
45042             if(!this.split){
45043                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
45044                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
45045                 /** The SplitBar for this region 
45046                 * @type Roo.SplitBar */
45047                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
45048                 this.split.on("moved", this.onSplitMove, this);
45049                 this.split.useShim = config.useShim === true;
45050                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45051                 if(this.useSplitTips){
45052                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45053                 }
45054                 if(config.collapsible){
45055                     this.split.el.on("dblclick", this.collapse,  this);
45056                 }
45057             }
45058             if(typeof config.minSize != "undefined"){
45059                 this.split.minSize = config.minSize;
45060             }
45061             if(typeof config.maxSize != "undefined"){
45062                 this.split.maxSize = config.maxSize;
45063             }
45064             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45065                 this.hideSplitter();
45066             }
45067         }
45068     },
45069
45070     getHMaxSize : function(){
45071          var cmax = this.config.maxSize || 10000;
45072          var center = this.mgr.getRegion("center");
45073          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45074     },
45075
45076     getVMaxSize : function(){
45077          var cmax = this.config.maxSize || 10000;
45078          var center = this.mgr.getRegion("center");
45079          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45080     },
45081
45082     onSplitMove : function(split, newSize){
45083         this.fireEvent("resized", this, newSize);
45084     },
45085     
45086     /** 
45087      * Returns the {@link Roo.SplitBar} for this region.
45088      * @return {Roo.SplitBar}
45089      */
45090     getSplitBar : function(){
45091         return this.split;
45092     },
45093     
45094     hide : function(){
45095         this.hideSplitter();
45096         Roo.SplitLayoutRegion.superclass.hide.call(this);
45097     },
45098
45099     hideSplitter : function(){
45100         if(this.split){
45101             this.split.el.setLocation(-2000,-2000);
45102             this.split.el.hide();
45103         }
45104     },
45105
45106     show : function(){
45107         if(this.split){
45108             this.split.el.show();
45109         }
45110         Roo.SplitLayoutRegion.superclass.show.call(this);
45111     },
45112     
45113     beforeSlide: function(){
45114         if(Roo.isGecko){// firefox overflow auto bug workaround
45115             this.bodyEl.clip();
45116             if(this.tabs) this.tabs.bodyEl.clip();
45117             if(this.activePanel){
45118                 this.activePanel.getEl().clip();
45119                 
45120                 if(this.activePanel.beforeSlide){
45121                     this.activePanel.beforeSlide();
45122                 }
45123             }
45124         }
45125     },
45126     
45127     afterSlide : function(){
45128         if(Roo.isGecko){// firefox overflow auto bug workaround
45129             this.bodyEl.unclip();
45130             if(this.tabs) this.tabs.bodyEl.unclip();
45131             if(this.activePanel){
45132                 this.activePanel.getEl().unclip();
45133                 if(this.activePanel.afterSlide){
45134                     this.activePanel.afterSlide();
45135                 }
45136             }
45137         }
45138     },
45139
45140     initAutoHide : function(){
45141         if(this.autoHide !== false){
45142             if(!this.autoHideHd){
45143                 var st = new Roo.util.DelayedTask(this.slideIn, this);
45144                 this.autoHideHd = {
45145                     "mouseout": function(e){
45146                         if(!e.within(this.el, true)){
45147                             st.delay(500);
45148                         }
45149                     },
45150                     "mouseover" : function(e){
45151                         st.cancel();
45152                     },
45153                     scope : this
45154                 };
45155             }
45156             this.el.on(this.autoHideHd);
45157         }
45158     },
45159
45160     clearAutoHide : function(){
45161         if(this.autoHide !== false){
45162             this.el.un("mouseout", this.autoHideHd.mouseout);
45163             this.el.un("mouseover", this.autoHideHd.mouseover);
45164         }
45165     },
45166
45167     clearMonitor : function(){
45168         Roo.get(document).un("click", this.slideInIf, this);
45169     },
45170
45171     // these names are backwards but not changed for compat
45172     slideOut : function(){
45173         if(this.isSlid || this.el.hasActiveFx()){
45174             return;
45175         }
45176         this.isSlid = true;
45177         if(this.collapseBtn){
45178             this.collapseBtn.hide();
45179         }
45180         this.closeBtnState = this.closeBtn.getStyle('display');
45181         this.closeBtn.hide();
45182         if(this.stickBtn){
45183             this.stickBtn.show();
45184         }
45185         this.el.show();
45186         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
45187         this.beforeSlide();
45188         this.el.setStyle("z-index", 10001);
45189         this.el.slideIn(this.getSlideAnchor(), {
45190             callback: function(){
45191                 this.afterSlide();
45192                 this.initAutoHide();
45193                 Roo.get(document).on("click", this.slideInIf, this);
45194                 this.fireEvent("slideshow", this);
45195             },
45196             scope: this,
45197             block: true
45198         });
45199     },
45200
45201     afterSlideIn : function(){
45202         this.clearAutoHide();
45203         this.isSlid = false;
45204         this.clearMonitor();
45205         this.el.setStyle("z-index", "");
45206         if(this.collapseBtn){
45207             this.collapseBtn.show();
45208         }
45209         this.closeBtn.setStyle('display', this.closeBtnState);
45210         if(this.stickBtn){
45211             this.stickBtn.hide();
45212         }
45213         this.fireEvent("slidehide", this);
45214     },
45215
45216     slideIn : function(cb){
45217         if(!this.isSlid || this.el.hasActiveFx()){
45218             Roo.callback(cb);
45219             return;
45220         }
45221         this.isSlid = false;
45222         this.beforeSlide();
45223         this.el.slideOut(this.getSlideAnchor(), {
45224             callback: function(){
45225                 this.el.setLeftTop(-10000, -10000);
45226                 this.afterSlide();
45227                 this.afterSlideIn();
45228                 Roo.callback(cb);
45229             },
45230             scope: this,
45231             block: true
45232         });
45233     },
45234     
45235     slideInIf : function(e){
45236         if(!e.within(this.el)){
45237             this.slideIn();
45238         }
45239     },
45240
45241     animateCollapse : function(){
45242         this.beforeSlide();
45243         this.el.setStyle("z-index", 20000);
45244         var anchor = this.getSlideAnchor();
45245         this.el.slideOut(anchor, {
45246             callback : function(){
45247                 this.el.setStyle("z-index", "");
45248                 this.collapsedEl.slideIn(anchor, {duration:.3});
45249                 this.afterSlide();
45250                 this.el.setLocation(-10000,-10000);
45251                 this.el.hide();
45252                 this.fireEvent("collapsed", this);
45253             },
45254             scope: this,
45255             block: true
45256         });
45257     },
45258
45259     animateExpand : function(){
45260         this.beforeSlide();
45261         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
45262         this.el.setStyle("z-index", 20000);
45263         this.collapsedEl.hide({
45264             duration:.1
45265         });
45266         this.el.slideIn(this.getSlideAnchor(), {
45267             callback : function(){
45268                 this.el.setStyle("z-index", "");
45269                 this.afterSlide();
45270                 if(this.split){
45271                     this.split.el.show();
45272                 }
45273                 this.fireEvent("invalidated", this);
45274                 this.fireEvent("expanded", this);
45275             },
45276             scope: this,
45277             block: true
45278         });
45279     },
45280
45281     anchors : {
45282         "west" : "left",
45283         "east" : "right",
45284         "north" : "top",
45285         "south" : "bottom"
45286     },
45287
45288     sanchors : {
45289         "west" : "l",
45290         "east" : "r",
45291         "north" : "t",
45292         "south" : "b"
45293     },
45294
45295     canchors : {
45296         "west" : "tl-tr",
45297         "east" : "tr-tl",
45298         "north" : "tl-bl",
45299         "south" : "bl-tl"
45300     },
45301
45302     getAnchor : function(){
45303         return this.anchors[this.position];
45304     },
45305
45306     getCollapseAnchor : function(){
45307         return this.canchors[this.position];
45308     },
45309
45310     getSlideAnchor : function(){
45311         return this.sanchors[this.position];
45312     },
45313
45314     getAlignAdj : function(){
45315         var cm = this.cmargins;
45316         switch(this.position){
45317             case "west":
45318                 return [0, 0];
45319             break;
45320             case "east":
45321                 return [0, 0];
45322             break;
45323             case "north":
45324                 return [0, 0];
45325             break;
45326             case "south":
45327                 return [0, 0];
45328             break;
45329         }
45330     },
45331
45332     getExpandAdj : function(){
45333         var c = this.collapsedEl, cm = this.cmargins;
45334         switch(this.position){
45335             case "west":
45336                 return [-(cm.right+c.getWidth()+cm.left), 0];
45337             break;
45338             case "east":
45339                 return [cm.right+c.getWidth()+cm.left, 0];
45340             break;
45341             case "north":
45342                 return [0, -(cm.top+cm.bottom+c.getHeight())];
45343             break;
45344             case "south":
45345                 return [0, cm.top+cm.bottom+c.getHeight()];
45346             break;
45347         }
45348     }
45349 });/*
45350  * Based on:
45351  * Ext JS Library 1.1.1
45352  * Copyright(c) 2006-2007, Ext JS, LLC.
45353  *
45354  * Originally Released Under LGPL - original licence link has changed is not relivant.
45355  *
45356  * Fork - LGPL
45357  * <script type="text/javascript">
45358  */
45359 /*
45360  * These classes are private internal classes
45361  */
45362 Roo.CenterLayoutRegion = function(mgr, config){
45363     Roo.LayoutRegion.call(this, mgr, config, "center");
45364     this.visible = true;
45365     this.minWidth = config.minWidth || 20;
45366     this.minHeight = config.minHeight || 20;
45367 };
45368
45369 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
45370     hide : function(){
45371         // center panel can't be hidden
45372     },
45373     
45374     show : function(){
45375         // center panel can't be hidden
45376     },
45377     
45378     getMinWidth: function(){
45379         return this.minWidth;
45380     },
45381     
45382     getMinHeight: function(){
45383         return this.minHeight;
45384     }
45385 });
45386
45387
45388 Roo.NorthLayoutRegion = function(mgr, config){
45389     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
45390     if(this.split){
45391         this.split.placement = Roo.SplitBar.TOP;
45392         this.split.orientation = Roo.SplitBar.VERTICAL;
45393         this.split.el.addClass("x-layout-split-v");
45394     }
45395     var size = config.initialSize || config.height;
45396     if(typeof size != "undefined"){
45397         this.el.setHeight(size);
45398     }
45399 };
45400 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
45401     orientation: Roo.SplitBar.VERTICAL,
45402     getBox : function(){
45403         if(this.collapsed){
45404             return this.collapsedEl.getBox();
45405         }
45406         var box = this.el.getBox();
45407         if(this.split){
45408             box.height += this.split.el.getHeight();
45409         }
45410         return box;
45411     },
45412     
45413     updateBox : function(box){
45414         if(this.split && !this.collapsed){
45415             box.height -= this.split.el.getHeight();
45416             this.split.el.setLeft(box.x);
45417             this.split.el.setTop(box.y+box.height);
45418             this.split.el.setWidth(box.width);
45419         }
45420         if(this.collapsed){
45421             this.updateBody(box.width, null);
45422         }
45423         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45424     }
45425 });
45426
45427 Roo.SouthLayoutRegion = function(mgr, config){
45428     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
45429     if(this.split){
45430         this.split.placement = Roo.SplitBar.BOTTOM;
45431         this.split.orientation = Roo.SplitBar.VERTICAL;
45432         this.split.el.addClass("x-layout-split-v");
45433     }
45434     var size = config.initialSize || config.height;
45435     if(typeof size != "undefined"){
45436         this.el.setHeight(size);
45437     }
45438 };
45439 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
45440     orientation: Roo.SplitBar.VERTICAL,
45441     getBox : function(){
45442         if(this.collapsed){
45443             return this.collapsedEl.getBox();
45444         }
45445         var box = this.el.getBox();
45446         if(this.split){
45447             var sh = this.split.el.getHeight();
45448             box.height += sh;
45449             box.y -= sh;
45450         }
45451         return box;
45452     },
45453     
45454     updateBox : function(box){
45455         if(this.split && !this.collapsed){
45456             var sh = this.split.el.getHeight();
45457             box.height -= sh;
45458             box.y += sh;
45459             this.split.el.setLeft(box.x);
45460             this.split.el.setTop(box.y-sh);
45461             this.split.el.setWidth(box.width);
45462         }
45463         if(this.collapsed){
45464             this.updateBody(box.width, null);
45465         }
45466         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45467     }
45468 });
45469
45470 Roo.EastLayoutRegion = function(mgr, config){
45471     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
45472     if(this.split){
45473         this.split.placement = Roo.SplitBar.RIGHT;
45474         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45475         this.split.el.addClass("x-layout-split-h");
45476     }
45477     var size = config.initialSize || config.width;
45478     if(typeof size != "undefined"){
45479         this.el.setWidth(size);
45480     }
45481 };
45482 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
45483     orientation: Roo.SplitBar.HORIZONTAL,
45484     getBox : function(){
45485         if(this.collapsed){
45486             return this.collapsedEl.getBox();
45487         }
45488         var box = this.el.getBox();
45489         if(this.split){
45490             var sw = this.split.el.getWidth();
45491             box.width += sw;
45492             box.x -= sw;
45493         }
45494         return box;
45495     },
45496
45497     updateBox : function(box){
45498         if(this.split && !this.collapsed){
45499             var sw = this.split.el.getWidth();
45500             box.width -= sw;
45501             this.split.el.setLeft(box.x);
45502             this.split.el.setTop(box.y);
45503             this.split.el.setHeight(box.height);
45504             box.x += sw;
45505         }
45506         if(this.collapsed){
45507             this.updateBody(null, box.height);
45508         }
45509         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45510     }
45511 });
45512
45513 Roo.WestLayoutRegion = function(mgr, config){
45514     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
45515     if(this.split){
45516         this.split.placement = Roo.SplitBar.LEFT;
45517         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45518         this.split.el.addClass("x-layout-split-h");
45519     }
45520     var size = config.initialSize || config.width;
45521     if(typeof size != "undefined"){
45522         this.el.setWidth(size);
45523     }
45524 };
45525 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
45526     orientation: Roo.SplitBar.HORIZONTAL,
45527     getBox : function(){
45528         if(this.collapsed){
45529             return this.collapsedEl.getBox();
45530         }
45531         var box = this.el.getBox();
45532         if(this.split){
45533             box.width += this.split.el.getWidth();
45534         }
45535         return box;
45536     },
45537     
45538     updateBox : function(box){
45539         if(this.split && !this.collapsed){
45540             var sw = this.split.el.getWidth();
45541             box.width -= sw;
45542             this.split.el.setLeft(box.x+box.width);
45543             this.split.el.setTop(box.y);
45544             this.split.el.setHeight(box.height);
45545         }
45546         if(this.collapsed){
45547             this.updateBody(null, box.height);
45548         }
45549         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45550     }
45551 });
45552 /*
45553  * Based on:
45554  * Ext JS Library 1.1.1
45555  * Copyright(c) 2006-2007, Ext JS, LLC.
45556  *
45557  * Originally Released Under LGPL - original licence link has changed is not relivant.
45558  *
45559  * Fork - LGPL
45560  * <script type="text/javascript">
45561  */
45562  
45563  
45564 /*
45565  * Private internal class for reading and applying state
45566  */
45567 Roo.LayoutStateManager = function(layout){
45568      // default empty state
45569      this.state = {
45570         north: {},
45571         south: {},
45572         east: {},
45573         west: {}       
45574     };
45575 };
45576
45577 Roo.LayoutStateManager.prototype = {
45578     init : function(layout, provider){
45579         this.provider = provider;
45580         var state = provider.get(layout.id+"-layout-state");
45581         if(state){
45582             var wasUpdating = layout.isUpdating();
45583             if(!wasUpdating){
45584                 layout.beginUpdate();
45585             }
45586             for(var key in state){
45587                 if(typeof state[key] != "function"){
45588                     var rstate = state[key];
45589                     var r = layout.getRegion(key);
45590                     if(r && rstate){
45591                         if(rstate.size){
45592                             r.resizeTo(rstate.size);
45593                         }
45594                         if(rstate.collapsed == true){
45595                             r.collapse(true);
45596                         }else{
45597                             r.expand(null, true);
45598                         }
45599                     }
45600                 }
45601             }
45602             if(!wasUpdating){
45603                 layout.endUpdate();
45604             }
45605             this.state = state; 
45606         }
45607         this.layout = layout;
45608         layout.on("regionresized", this.onRegionResized, this);
45609         layout.on("regioncollapsed", this.onRegionCollapsed, this);
45610         layout.on("regionexpanded", this.onRegionExpanded, this);
45611     },
45612     
45613     storeState : function(){
45614         this.provider.set(this.layout.id+"-layout-state", this.state);
45615     },
45616     
45617     onRegionResized : function(region, newSize){
45618         this.state[region.getPosition()].size = newSize;
45619         this.storeState();
45620     },
45621     
45622     onRegionCollapsed : function(region){
45623         this.state[region.getPosition()].collapsed = true;
45624         this.storeState();
45625     },
45626     
45627     onRegionExpanded : function(region){
45628         this.state[region.getPosition()].collapsed = false;
45629         this.storeState();
45630     }
45631 };/*
45632  * Based on:
45633  * Ext JS Library 1.1.1
45634  * Copyright(c) 2006-2007, Ext JS, LLC.
45635  *
45636  * Originally Released Under LGPL - original licence link has changed is not relivant.
45637  *
45638  * Fork - LGPL
45639  * <script type="text/javascript">
45640  */
45641 /**
45642  * @class Roo.ContentPanel
45643  * @extends Roo.util.Observable
45644  * A basic ContentPanel element.
45645  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
45646  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
45647  * @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
45648  * @cfg {Boolean}   closable      True if the panel can be closed/removed
45649  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
45650  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45651  * @cfg {Toolbar}   toolbar       A toolbar for this panel
45652  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
45653  * @cfg {String} title          The title for this panel
45654  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45655  * @cfg {String} url            Calls {@link #setUrl} with this value
45656  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45657  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
45658  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
45659  * @cfg {String} content        Raw content to fill content panel with (uses setContent on construction.)
45660
45661  * @constructor
45662  * Create a new ContentPanel.
45663  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
45664  * @param {String/Object} config A string to set only the title or a config object
45665  * @param {String} content (optional) Set the HTML content for this panel
45666  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
45667  */
45668 Roo.ContentPanel = function(el, config, content){
45669     
45670      
45671     /*
45672     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
45673         config = el;
45674         el = Roo.id();
45675     }
45676     if (config && config.parentLayout) { 
45677         el = config.parentLayout.el.createChild(); 
45678     }
45679     */
45680     if(el.autoCreate){ // xtype is available if this is called from factory
45681         config = el;
45682         el = Roo.id();
45683     }
45684     this.el = Roo.get(el);
45685     if(!this.el && config && config.autoCreate){
45686         if(typeof config.autoCreate == "object"){
45687             if(!config.autoCreate.id){
45688                 config.autoCreate.id = config.id||el;
45689             }
45690             this.el = Roo.DomHelper.append(document.body,
45691                         config.autoCreate, true);
45692         }else{
45693             this.el = Roo.DomHelper.append(document.body,
45694                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
45695         }
45696     }
45697     this.closable = false;
45698     this.loaded = false;
45699     this.active = false;
45700     if(typeof config == "string"){
45701         this.title = config;
45702     }else{
45703         Roo.apply(this, config);
45704     }
45705     
45706     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
45707         this.wrapEl = this.el.wrap();    
45708         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
45709         
45710     }
45711     
45712     
45713     
45714     if(this.resizeEl){
45715         this.resizeEl = Roo.get(this.resizeEl, true);
45716     }else{
45717         this.resizeEl = this.el;
45718     }
45719     this.addEvents({
45720         /**
45721          * @event activate
45722          * Fires when this panel is activated. 
45723          * @param {Roo.ContentPanel} this
45724          */
45725         "activate" : true,
45726         /**
45727          * @event deactivate
45728          * Fires when this panel is activated. 
45729          * @param {Roo.ContentPanel} this
45730          */
45731         "deactivate" : true,
45732
45733         /**
45734          * @event resize
45735          * Fires when this panel is resized if fitToFrame is true.
45736          * @param {Roo.ContentPanel} this
45737          * @param {Number} width The width after any component adjustments
45738          * @param {Number} height The height after any component adjustments
45739          */
45740         "resize" : true
45741     });
45742     if(this.autoScroll){
45743         this.resizeEl.setStyle("overflow", "auto");
45744     } else {
45745         // fix randome scrolling
45746         this.el.on('scroll', function() {
45747             Roo.log('fix random scolling');
45748             this.scrollTo('top',0); 
45749         });
45750     }
45751     content = content || this.content;
45752     if(content){
45753         this.setContent(content);
45754     }
45755     if(config && config.url){
45756         this.setUrl(this.url, this.params, this.loadOnce);
45757     }
45758     
45759     
45760     
45761     Roo.ContentPanel.superclass.constructor.call(this);
45762 };
45763
45764 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
45765     tabTip:'',
45766     setRegion : function(region){
45767         this.region = region;
45768         if(region){
45769            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
45770         }else{
45771            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
45772         } 
45773     },
45774     
45775     /**
45776      * Returns the toolbar for this Panel if one was configured. 
45777      * @return {Roo.Toolbar} 
45778      */
45779     getToolbar : function(){
45780         return this.toolbar;
45781     },
45782     
45783     setActiveState : function(active){
45784         this.active = active;
45785         if(!active){
45786             this.fireEvent("deactivate", this);
45787         }else{
45788             this.fireEvent("activate", this);
45789         }
45790     },
45791     /**
45792      * Updates this panel's element
45793      * @param {String} content The new content
45794      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45795     */
45796     setContent : function(content, loadScripts){
45797         this.el.update(content, loadScripts);
45798     },
45799
45800     ignoreResize : function(w, h){
45801         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45802             return true;
45803         }else{
45804             this.lastSize = {width: w, height: h};
45805             return false;
45806         }
45807     },
45808     /**
45809      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45810      * @return {Roo.UpdateManager} The UpdateManager
45811      */
45812     getUpdateManager : function(){
45813         return this.el.getUpdateManager();
45814     },
45815      /**
45816      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45817      * @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:
45818 <pre><code>
45819 panel.load({
45820     url: "your-url.php",
45821     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45822     callback: yourFunction,
45823     scope: yourObject, //(optional scope)
45824     discardUrl: false,
45825     nocache: false,
45826     text: "Loading...",
45827     timeout: 30,
45828     scripts: false
45829 });
45830 </code></pre>
45831      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45832      * 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.
45833      * @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}
45834      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45835      * @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.
45836      * @return {Roo.ContentPanel} this
45837      */
45838     load : function(){
45839         var um = this.el.getUpdateManager();
45840         um.update.apply(um, arguments);
45841         return this;
45842     },
45843
45844
45845     /**
45846      * 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.
45847      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45848      * @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)
45849      * @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)
45850      * @return {Roo.UpdateManager} The UpdateManager
45851      */
45852     setUrl : function(url, params, loadOnce){
45853         if(this.refreshDelegate){
45854             this.removeListener("activate", this.refreshDelegate);
45855         }
45856         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45857         this.on("activate", this.refreshDelegate);
45858         return this.el.getUpdateManager();
45859     },
45860     
45861     _handleRefresh : function(url, params, loadOnce){
45862         if(!loadOnce || !this.loaded){
45863             var updater = this.el.getUpdateManager();
45864             updater.update(url, params, this._setLoaded.createDelegate(this));
45865         }
45866     },
45867     
45868     _setLoaded : function(){
45869         this.loaded = true;
45870     }, 
45871     
45872     /**
45873      * Returns this panel's id
45874      * @return {String} 
45875      */
45876     getId : function(){
45877         return this.el.id;
45878     },
45879     
45880     /** 
45881      * Returns this panel's element - used by regiosn to add.
45882      * @return {Roo.Element} 
45883      */
45884     getEl : function(){
45885         return this.wrapEl || this.el;
45886     },
45887     
45888     adjustForComponents : function(width, height){
45889         if(this.resizeEl != this.el){
45890             width -= this.el.getFrameWidth('lr');
45891             height -= this.el.getFrameWidth('tb');
45892         }
45893         if(this.toolbar){
45894             var te = this.toolbar.getEl();
45895             height -= te.getHeight();
45896             te.setWidth(width);
45897         }
45898         if(this.adjustments){
45899             width += this.adjustments[0];
45900             height += this.adjustments[1];
45901         }
45902         return {"width": width, "height": height};
45903     },
45904     
45905     setSize : function(width, height){
45906         if(this.fitToFrame && !this.ignoreResize(width, height)){
45907             if(this.fitContainer && this.resizeEl != this.el){
45908                 this.el.setSize(width, height);
45909             }
45910             var size = this.adjustForComponents(width, height);
45911             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45912             this.fireEvent('resize', this, size.width, size.height);
45913         }
45914     },
45915     
45916     /**
45917      * Returns this panel's title
45918      * @return {String} 
45919      */
45920     getTitle : function(){
45921         return this.title;
45922     },
45923     
45924     /**
45925      * Set this panel's title
45926      * @param {String} title
45927      */
45928     setTitle : function(title){
45929         this.title = title;
45930         if(this.region){
45931             this.region.updatePanelTitle(this, title);
45932         }
45933     },
45934     
45935     /**
45936      * Returns true is this panel was configured to be closable
45937      * @return {Boolean} 
45938      */
45939     isClosable : function(){
45940         return this.closable;
45941     },
45942     
45943     beforeSlide : function(){
45944         this.el.clip();
45945         this.resizeEl.clip();
45946     },
45947     
45948     afterSlide : function(){
45949         this.el.unclip();
45950         this.resizeEl.unclip();
45951     },
45952     
45953     /**
45954      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45955      *   Will fail silently if the {@link #setUrl} method has not been called.
45956      *   This does not activate the panel, just updates its content.
45957      */
45958     refresh : function(){
45959         if(this.refreshDelegate){
45960            this.loaded = false;
45961            this.refreshDelegate();
45962         }
45963     },
45964     
45965     /**
45966      * Destroys this panel
45967      */
45968     destroy : function(){
45969         this.el.removeAllListeners();
45970         var tempEl = document.createElement("span");
45971         tempEl.appendChild(this.el.dom);
45972         tempEl.innerHTML = "";
45973         this.el.remove();
45974         this.el = null;
45975     },
45976     
45977     /**
45978      * form - if the content panel contains a form - this is a reference to it.
45979      * @type {Roo.form.Form}
45980      */
45981     form : false,
45982     /**
45983      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
45984      *    This contains a reference to it.
45985      * @type {Roo.View}
45986      */
45987     view : false,
45988     
45989       /**
45990      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45991      * <pre><code>
45992
45993 layout.addxtype({
45994        xtype : 'Form',
45995        items: [ .... ]
45996    }
45997 );
45998
45999 </code></pre>
46000      * @param {Object} cfg Xtype definition of item to add.
46001      */
46002     
46003     addxtype : function(cfg) {
46004         // add form..
46005         if (cfg.xtype.match(/^Form$/)) {
46006             var el = this.el.createChild();
46007
46008             this.form = new  Roo.form.Form(cfg);
46009             
46010             
46011             if ( this.form.allItems.length) this.form.render(el.dom);
46012             return this.form;
46013         }
46014         // should only have one of theses..
46015         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
46016             // views..
46017             cfg.el = this.el.appendChild(document.createElement("div"));
46018             // factory?
46019             var ret = new Roo[cfg.xtype](cfg);
46020             ret.render && ret.render(false, ''); // render blank..
46021             this.view = ret;
46022             return ret;
46023         }
46024         return false;
46025     }
46026 });
46027
46028 /**
46029  * @class Roo.GridPanel
46030  * @extends Roo.ContentPanel
46031  * @constructor
46032  * Create a new GridPanel.
46033  * @param {Roo.grid.Grid} grid The grid for this panel
46034  * @param {String/Object} config A string to set only the panel's title, or a config object
46035  */
46036 Roo.GridPanel = function(grid, config){
46037     
46038   
46039     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
46040         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
46041         
46042     this.wrapper.dom.appendChild(grid.getGridEl().dom);
46043     
46044     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
46045     
46046     if(this.toolbar){
46047         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
46048     }
46049     // xtype created footer. - not sure if will work as we normally have to render first..
46050     if (this.footer && !this.footer.el && this.footer.xtype) {
46051         
46052         this.footer.container = this.grid.getView().getFooterPanel(true);
46053         this.footer.dataSource = this.grid.dataSource;
46054         this.footer = Roo.factory(this.footer, Roo);
46055         
46056     }
46057     
46058     grid.monitorWindowResize = false; // turn off autosizing
46059     grid.autoHeight = false;
46060     grid.autoWidth = false;
46061     this.grid = grid;
46062     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
46063 };
46064
46065 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
46066     getId : function(){
46067         return this.grid.id;
46068     },
46069     
46070     /**
46071      * Returns the grid for this panel
46072      * @return {Roo.grid.Grid} 
46073      */
46074     getGrid : function(){
46075         return this.grid;    
46076     },
46077     
46078     setSize : function(width, height){
46079         if(!this.ignoreResize(width, height)){
46080             var grid = this.grid;
46081             var size = this.adjustForComponents(width, height);
46082             grid.getGridEl().setSize(size.width, size.height);
46083             grid.autoSize();
46084         }
46085     },
46086     
46087     beforeSlide : function(){
46088         this.grid.getView().scroller.clip();
46089     },
46090     
46091     afterSlide : function(){
46092         this.grid.getView().scroller.unclip();
46093     },
46094     
46095     destroy : function(){
46096         this.grid.destroy();
46097         delete this.grid;
46098         Roo.GridPanel.superclass.destroy.call(this); 
46099     }
46100 });
46101
46102
46103 /**
46104  * @class Roo.NestedLayoutPanel
46105  * @extends Roo.ContentPanel
46106  * @constructor
46107  * Create a new NestedLayoutPanel.
46108  * 
46109  * 
46110  * @param {Roo.BorderLayout} layout The layout for this panel
46111  * @param {String/Object} config A string to set only the title or a config object
46112  */
46113 Roo.NestedLayoutPanel = function(layout, config)
46114 {
46115     // construct with only one argument..
46116     /* FIXME - implement nicer consturctors
46117     if (layout.layout) {
46118         config = layout;
46119         layout = config.layout;
46120         delete config.layout;
46121     }
46122     if (layout.xtype && !layout.getEl) {
46123         // then layout needs constructing..
46124         layout = Roo.factory(layout, Roo);
46125     }
46126     */
46127     
46128     
46129     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
46130     
46131     layout.monitorWindowResize = false; // turn off autosizing
46132     this.layout = layout;
46133     this.layout.getEl().addClass("x-layout-nested-layout");
46134     
46135     
46136     
46137     
46138 };
46139
46140 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
46141
46142     setSize : function(width, height){
46143         if(!this.ignoreResize(width, height)){
46144             var size = this.adjustForComponents(width, height);
46145             var el = this.layout.getEl();
46146             el.setSize(size.width, size.height);
46147             var touch = el.dom.offsetWidth;
46148             this.layout.layout();
46149             // ie requires a double layout on the first pass
46150             if(Roo.isIE && !this.initialized){
46151                 this.initialized = true;
46152                 this.layout.layout();
46153             }
46154         }
46155     },
46156     
46157     // activate all subpanels if not currently active..
46158     
46159     setActiveState : function(active){
46160         this.active = active;
46161         if(!active){
46162             this.fireEvent("deactivate", this);
46163             return;
46164         }
46165         
46166         this.fireEvent("activate", this);
46167         // not sure if this should happen before or after..
46168         if (!this.layout) {
46169             return; // should not happen..
46170         }
46171         var reg = false;
46172         for (var r in this.layout.regions) {
46173             reg = this.layout.getRegion(r);
46174             if (reg.getActivePanel()) {
46175                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
46176                 reg.setActivePanel(reg.getActivePanel());
46177                 continue;
46178             }
46179             if (!reg.panels.length) {
46180                 continue;
46181             }
46182             reg.showPanel(reg.getPanel(0));
46183         }
46184         
46185         
46186         
46187         
46188     },
46189     
46190     /**
46191      * Returns the nested BorderLayout for this panel
46192      * @return {Roo.BorderLayout} 
46193      */
46194     getLayout : function(){
46195         return this.layout;
46196     },
46197     
46198      /**
46199      * Adds a xtype elements to the layout of the nested panel
46200      * <pre><code>
46201
46202 panel.addxtype({
46203        xtype : 'ContentPanel',
46204        region: 'west',
46205        items: [ .... ]
46206    }
46207 );
46208
46209 panel.addxtype({
46210         xtype : 'NestedLayoutPanel',
46211         region: 'west',
46212         layout: {
46213            center: { },
46214            west: { }   
46215         },
46216         items : [ ... list of content panels or nested layout panels.. ]
46217    }
46218 );
46219 </code></pre>
46220      * @param {Object} cfg Xtype definition of item to add.
46221      */
46222     addxtype : function(cfg) {
46223         return this.layout.addxtype(cfg);
46224     
46225     }
46226 });
46227
46228 Roo.ScrollPanel = function(el, config, content){
46229     config = config || {};
46230     config.fitToFrame = true;
46231     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
46232     
46233     this.el.dom.style.overflow = "hidden";
46234     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
46235     this.el.removeClass("x-layout-inactive-content");
46236     this.el.on("mousewheel", this.onWheel, this);
46237
46238     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
46239     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
46240     up.unselectable(); down.unselectable();
46241     up.on("click", this.scrollUp, this);
46242     down.on("click", this.scrollDown, this);
46243     up.addClassOnOver("x-scroller-btn-over");
46244     down.addClassOnOver("x-scroller-btn-over");
46245     up.addClassOnClick("x-scroller-btn-click");
46246     down.addClassOnClick("x-scroller-btn-click");
46247     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
46248
46249     this.resizeEl = this.el;
46250     this.el = wrap; this.up = up; this.down = down;
46251 };
46252
46253 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
46254     increment : 100,
46255     wheelIncrement : 5,
46256     scrollUp : function(){
46257         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
46258     },
46259
46260     scrollDown : function(){
46261         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
46262     },
46263
46264     afterScroll : function(){
46265         var el = this.resizeEl;
46266         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
46267         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
46268         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
46269     },
46270
46271     setSize : function(){
46272         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
46273         this.afterScroll();
46274     },
46275
46276     onWheel : function(e){
46277         var d = e.getWheelDelta();
46278         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
46279         this.afterScroll();
46280         e.stopEvent();
46281     },
46282
46283     setContent : function(content, loadScripts){
46284         this.resizeEl.update(content, loadScripts);
46285     }
46286
46287 });
46288
46289
46290
46291
46292
46293
46294
46295
46296
46297 /**
46298  * @class Roo.TreePanel
46299  * @extends Roo.ContentPanel
46300  * @constructor
46301  * Create a new TreePanel. - defaults to fit/scoll contents.
46302  * @param {String/Object} config A string to set only the panel's title, or a config object
46303  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
46304  */
46305 Roo.TreePanel = function(config){
46306     var el = config.el;
46307     var tree = config.tree;
46308     delete config.tree; 
46309     delete config.el; // hopefull!
46310     
46311     // wrapper for IE7 strict & safari scroll issue
46312     
46313     var treeEl = el.createChild();
46314     config.resizeEl = treeEl;
46315     
46316     
46317     
46318     Roo.TreePanel.superclass.constructor.call(this, el, config);
46319  
46320  
46321     this.tree = new Roo.tree.TreePanel(treeEl , tree);
46322     //console.log(tree);
46323     this.on('activate', function()
46324     {
46325         if (this.tree.rendered) {
46326             return;
46327         }
46328         //console.log('render tree');
46329         this.tree.render();
46330     });
46331     
46332     this.on('resize',  function (cp, w, h) {
46333             this.tree.innerCt.setWidth(w);
46334             this.tree.innerCt.setHeight(h);
46335             this.tree.innerCt.setStyle('overflow-y', 'auto');
46336     });
46337
46338         
46339     
46340 };
46341
46342 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
46343     fitToFrame : true,
46344     autoScroll : true
46345 });
46346
46347
46348
46349
46350
46351
46352
46353
46354
46355
46356
46357 /*
46358  * Based on:
46359  * Ext JS Library 1.1.1
46360  * Copyright(c) 2006-2007, Ext JS, LLC.
46361  *
46362  * Originally Released Under LGPL - original licence link has changed is not relivant.
46363  *
46364  * Fork - LGPL
46365  * <script type="text/javascript">
46366  */
46367  
46368
46369 /**
46370  * @class Roo.ReaderLayout
46371  * @extends Roo.BorderLayout
46372  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
46373  * center region containing two nested regions (a top one for a list view and one for item preview below),
46374  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
46375  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
46376  * expedites the setup of the overall layout and regions for this common application style.
46377  * Example:
46378  <pre><code>
46379 var reader = new Roo.ReaderLayout();
46380 var CP = Roo.ContentPanel;  // shortcut for adding
46381
46382 reader.beginUpdate();
46383 reader.add("north", new CP("north", "North"));
46384 reader.add("west", new CP("west", {title: "West"}));
46385 reader.add("east", new CP("east", {title: "East"}));
46386
46387 reader.regions.listView.add(new CP("listView", "List"));
46388 reader.regions.preview.add(new CP("preview", "Preview"));
46389 reader.endUpdate();
46390 </code></pre>
46391 * @constructor
46392 * Create a new ReaderLayout
46393 * @param {Object} config Configuration options
46394 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
46395 * document.body if omitted)
46396 */
46397 Roo.ReaderLayout = function(config, renderTo){
46398     var c = config || {size:{}};
46399     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
46400         north: c.north !== false ? Roo.apply({
46401             split:false,
46402             initialSize: 32,
46403             titlebar: false
46404         }, c.north) : false,
46405         west: c.west !== false ? Roo.apply({
46406             split:true,
46407             initialSize: 200,
46408             minSize: 175,
46409             maxSize: 400,
46410             titlebar: true,
46411             collapsible: true,
46412             animate: true,
46413             margins:{left:5,right:0,bottom:5,top:5},
46414             cmargins:{left:5,right:5,bottom:5,top:5}
46415         }, c.west) : false,
46416         east: c.east !== false ? Roo.apply({
46417             split:true,
46418             initialSize: 200,
46419             minSize: 175,
46420             maxSize: 400,
46421             titlebar: true,
46422             collapsible: true,
46423             animate: true,
46424             margins:{left:0,right:5,bottom:5,top:5},
46425             cmargins:{left:5,right:5,bottom:5,top:5}
46426         }, c.east) : false,
46427         center: Roo.apply({
46428             tabPosition: 'top',
46429             autoScroll:false,
46430             closeOnTab: true,
46431             titlebar:false,
46432             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
46433         }, c.center)
46434     });
46435
46436     this.el.addClass('x-reader');
46437
46438     this.beginUpdate();
46439
46440     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
46441         south: c.preview !== false ? Roo.apply({
46442             split:true,
46443             initialSize: 200,
46444             minSize: 100,
46445             autoScroll:true,
46446             collapsible:true,
46447             titlebar: true,
46448             cmargins:{top:5,left:0, right:0, bottom:0}
46449         }, c.preview) : false,
46450         center: Roo.apply({
46451             autoScroll:false,
46452             titlebar:false,
46453             minHeight:200
46454         }, c.listView)
46455     });
46456     this.add('center', new Roo.NestedLayoutPanel(inner,
46457             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
46458
46459     this.endUpdate();
46460
46461     this.regions.preview = inner.getRegion('south');
46462     this.regions.listView = inner.getRegion('center');
46463 };
46464
46465 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
46466  * Based on:
46467  * Ext JS Library 1.1.1
46468  * Copyright(c) 2006-2007, Ext JS, LLC.
46469  *
46470  * Originally Released Under LGPL - original licence link has changed is not relivant.
46471  *
46472  * Fork - LGPL
46473  * <script type="text/javascript">
46474  */
46475  
46476 /**
46477  * @class Roo.grid.Grid
46478  * @extends Roo.util.Observable
46479  * This class represents the primary interface of a component based grid control.
46480  * <br><br>Usage:<pre><code>
46481  var grid = new Roo.grid.Grid("my-container-id", {
46482      ds: myDataStore,
46483      cm: myColModel,
46484      selModel: mySelectionModel,
46485      autoSizeColumns: true,
46486      monitorWindowResize: false,
46487      trackMouseOver: true
46488  });
46489  // set any options
46490  grid.render();
46491  * </code></pre>
46492  * <b>Common Problems:</b><br/>
46493  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
46494  * element will correct this<br/>
46495  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
46496  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
46497  * are unpredictable.<br/>
46498  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
46499  * grid to calculate dimensions/offsets.<br/>
46500   * @constructor
46501  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
46502  * The container MUST have some type of size defined for the grid to fill. The container will be
46503  * automatically set to position relative if it isn't already.
46504  * @param {Object} config A config object that sets properties on this grid.
46505  */
46506 Roo.grid.Grid = function(container, config){
46507         // initialize the container
46508         this.container = Roo.get(container);
46509         this.container.update("");
46510         this.container.setStyle("overflow", "hidden");
46511     this.container.addClass('x-grid-container');
46512
46513     this.id = this.container.id;
46514
46515     Roo.apply(this, config);
46516     // check and correct shorthanded configs
46517     if(this.ds){
46518         this.dataSource = this.ds;
46519         delete this.ds;
46520     }
46521     if(this.cm){
46522         this.colModel = this.cm;
46523         delete this.cm;
46524     }
46525     if(this.sm){
46526         this.selModel = this.sm;
46527         delete this.sm;
46528     }
46529
46530     if (this.selModel) {
46531         this.selModel = Roo.factory(this.selModel, Roo.grid);
46532         this.sm = this.selModel;
46533         this.sm.xmodule = this.xmodule || false;
46534     }
46535     if (typeof(this.colModel.config) == 'undefined') {
46536         this.colModel = new Roo.grid.ColumnModel(this.colModel);
46537         this.cm = this.colModel;
46538         this.cm.xmodule = this.xmodule || false;
46539     }
46540     if (this.dataSource) {
46541         this.dataSource= Roo.factory(this.dataSource, Roo.data);
46542         this.ds = this.dataSource;
46543         this.ds.xmodule = this.xmodule || false;
46544          
46545     }
46546     
46547     
46548     
46549     if(this.width){
46550         this.container.setWidth(this.width);
46551     }
46552
46553     if(this.height){
46554         this.container.setHeight(this.height);
46555     }
46556     /** @private */
46557         this.addEvents({
46558         // raw events
46559         /**
46560          * @event click
46561          * The raw click event for the entire grid.
46562          * @param {Roo.EventObject} e
46563          */
46564         "click" : true,
46565         /**
46566          * @event dblclick
46567          * The raw dblclick event for the entire grid.
46568          * @param {Roo.EventObject} e
46569          */
46570         "dblclick" : true,
46571         /**
46572          * @event contextmenu
46573          * The raw contextmenu event for the entire grid.
46574          * @param {Roo.EventObject} e
46575          */
46576         "contextmenu" : true,
46577         /**
46578          * @event mousedown
46579          * The raw mousedown event for the entire grid.
46580          * @param {Roo.EventObject} e
46581          */
46582         "mousedown" : true,
46583         /**
46584          * @event mouseup
46585          * The raw mouseup event for the entire grid.
46586          * @param {Roo.EventObject} e
46587          */
46588         "mouseup" : true,
46589         /**
46590          * @event mouseover
46591          * The raw mouseover event for the entire grid.
46592          * @param {Roo.EventObject} e
46593          */
46594         "mouseover" : true,
46595         /**
46596          * @event mouseout
46597          * The raw mouseout event for the entire grid.
46598          * @param {Roo.EventObject} e
46599          */
46600         "mouseout" : true,
46601         /**
46602          * @event keypress
46603          * The raw keypress event for the entire grid.
46604          * @param {Roo.EventObject} e
46605          */
46606         "keypress" : true,
46607         /**
46608          * @event keydown
46609          * The raw keydown event for the entire grid.
46610          * @param {Roo.EventObject} e
46611          */
46612         "keydown" : true,
46613
46614         // custom events
46615
46616         /**
46617          * @event cellclick
46618          * Fires when a cell is clicked
46619          * @param {Grid} this
46620          * @param {Number} rowIndex
46621          * @param {Number} columnIndex
46622          * @param {Roo.EventObject} e
46623          */
46624         "cellclick" : true,
46625         /**
46626          * @event celldblclick
46627          * Fires when a cell is double clicked
46628          * @param {Grid} this
46629          * @param {Number} rowIndex
46630          * @param {Number} columnIndex
46631          * @param {Roo.EventObject} e
46632          */
46633         "celldblclick" : true,
46634         /**
46635          * @event rowclick
46636          * Fires when a row is clicked
46637          * @param {Grid} this
46638          * @param {Number} rowIndex
46639          * @param {Roo.EventObject} e
46640          */
46641         "rowclick" : true,
46642         /**
46643          * @event rowdblclick
46644          * Fires when a row is double clicked
46645          * @param {Grid} this
46646          * @param {Number} rowIndex
46647          * @param {Roo.EventObject} e
46648          */
46649         "rowdblclick" : true,
46650         /**
46651          * @event headerclick
46652          * Fires when a header is clicked
46653          * @param {Grid} this
46654          * @param {Number} columnIndex
46655          * @param {Roo.EventObject} e
46656          */
46657         "headerclick" : true,
46658         /**
46659          * @event headerdblclick
46660          * Fires when a header cell is double clicked
46661          * @param {Grid} this
46662          * @param {Number} columnIndex
46663          * @param {Roo.EventObject} e
46664          */
46665         "headerdblclick" : true,
46666         /**
46667          * @event rowcontextmenu
46668          * Fires when a row is right clicked
46669          * @param {Grid} this
46670          * @param {Number} rowIndex
46671          * @param {Roo.EventObject} e
46672          */
46673         "rowcontextmenu" : true,
46674         /**
46675          * @event cellcontextmenu
46676          * Fires when a cell is right clicked
46677          * @param {Grid} this
46678          * @param {Number} rowIndex
46679          * @param {Number} cellIndex
46680          * @param {Roo.EventObject} e
46681          */
46682          "cellcontextmenu" : true,
46683         /**
46684          * @event headercontextmenu
46685          * Fires when a header is right clicked
46686          * @param {Grid} this
46687          * @param {Number} columnIndex
46688          * @param {Roo.EventObject} e
46689          */
46690         "headercontextmenu" : true,
46691         /**
46692          * @event bodyscroll
46693          * Fires when the body element is scrolled
46694          * @param {Number} scrollLeft
46695          * @param {Number} scrollTop
46696          */
46697         "bodyscroll" : true,
46698         /**
46699          * @event columnresize
46700          * Fires when the user resizes a column
46701          * @param {Number} columnIndex
46702          * @param {Number} newSize
46703          */
46704         "columnresize" : true,
46705         /**
46706          * @event columnmove
46707          * Fires when the user moves a column
46708          * @param {Number} oldIndex
46709          * @param {Number} newIndex
46710          */
46711         "columnmove" : true,
46712         /**
46713          * @event startdrag
46714          * Fires when row(s) start being dragged
46715          * @param {Grid} this
46716          * @param {Roo.GridDD} dd The drag drop object
46717          * @param {event} e The raw browser event
46718          */
46719         "startdrag" : true,
46720         /**
46721          * @event enddrag
46722          * Fires when a drag operation is complete
46723          * @param {Grid} this
46724          * @param {Roo.GridDD} dd The drag drop object
46725          * @param {event} e The raw browser event
46726          */
46727         "enddrag" : true,
46728         /**
46729          * @event dragdrop
46730          * Fires when dragged row(s) are dropped on a valid DD target
46731          * @param {Grid} this
46732          * @param {Roo.GridDD} dd The drag drop object
46733          * @param {String} targetId The target drag drop object
46734          * @param {event} e The raw browser event
46735          */
46736         "dragdrop" : true,
46737         /**
46738          * @event dragover
46739          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
46740          * @param {Grid} this
46741          * @param {Roo.GridDD} dd The drag drop object
46742          * @param {String} targetId The target drag drop object
46743          * @param {event} e The raw browser event
46744          */
46745         "dragover" : true,
46746         /**
46747          * @event dragenter
46748          *  Fires when the dragged row(s) first cross another DD target while being dragged
46749          * @param {Grid} this
46750          * @param {Roo.GridDD} dd The drag drop object
46751          * @param {String} targetId The target drag drop object
46752          * @param {event} e The raw browser event
46753          */
46754         "dragenter" : true,
46755         /**
46756          * @event dragout
46757          * Fires when the dragged row(s) leave another DD target while being dragged
46758          * @param {Grid} this
46759          * @param {Roo.GridDD} dd The drag drop object
46760          * @param {String} targetId The target drag drop object
46761          * @param {event} e The raw browser event
46762          */
46763         "dragout" : true,
46764         /**
46765          * @event rowclass
46766          * Fires when a row is rendered, so you can change add a style to it.
46767          * @param {GridView} gridview   The grid view
46768          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
46769          */
46770         'rowclass' : true,
46771
46772         /**
46773          * @event render
46774          * Fires when the grid is rendered
46775          * @param {Grid} grid
46776          */
46777         'render' : true
46778     });
46779
46780     Roo.grid.Grid.superclass.constructor.call(this);
46781 };
46782 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
46783     
46784     /**
46785      * @cfg {String} ddGroup - drag drop group.
46786      */
46787
46788     /**
46789      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
46790      */
46791     minColumnWidth : 25,
46792
46793     /**
46794      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
46795      * <b>on initial render.</b> It is more efficient to explicitly size the columns
46796      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
46797      */
46798     autoSizeColumns : false,
46799
46800     /**
46801      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
46802      */
46803     autoSizeHeaders : true,
46804
46805     /**
46806      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
46807      */
46808     monitorWindowResize : true,
46809
46810     /**
46811      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
46812      * rows measured to get a columns size. Default is 0 (all rows).
46813      */
46814     maxRowsToMeasure : 0,
46815
46816     /**
46817      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
46818      */
46819     trackMouseOver : true,
46820
46821     /**
46822     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
46823     */
46824     
46825     /**
46826     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
46827     */
46828     enableDragDrop : false,
46829     
46830     /**
46831     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
46832     */
46833     enableColumnMove : true,
46834     
46835     /**
46836     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
46837     */
46838     enableColumnHide : true,
46839     
46840     /**
46841     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
46842     */
46843     enableRowHeightSync : false,
46844     
46845     /**
46846     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
46847     */
46848     stripeRows : true,
46849     
46850     /**
46851     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
46852     */
46853     autoHeight : false,
46854
46855     /**
46856      * @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.
46857      */
46858     autoExpandColumn : false,
46859
46860     /**
46861     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
46862     * Default is 50.
46863     */
46864     autoExpandMin : 50,
46865
46866     /**
46867     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
46868     */
46869     autoExpandMax : 1000,
46870
46871     /**
46872     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
46873     */
46874     view : null,
46875
46876     /**
46877     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
46878     */
46879     loadMask : false,
46880     /**
46881     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
46882     */
46883     dropTarget: false,
46884     
46885    
46886     
46887     // private
46888     rendered : false,
46889
46890     /**
46891     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
46892     * of a fixed width. Default is false.
46893     */
46894     /**
46895     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
46896     */
46897     /**
46898      * Called once after all setup has been completed and the grid is ready to be rendered.
46899      * @return {Roo.grid.Grid} this
46900      */
46901     render : function()
46902     {
46903         var c = this.container;
46904         // try to detect autoHeight/width mode
46905         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
46906             this.autoHeight = true;
46907         }
46908         var view = this.getView();
46909         view.init(this);
46910
46911         c.on("click", this.onClick, this);
46912         c.on("dblclick", this.onDblClick, this);
46913         c.on("contextmenu", this.onContextMenu, this);
46914         c.on("keydown", this.onKeyDown, this);
46915
46916         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
46917
46918         this.getSelectionModel().init(this);
46919
46920         view.render();
46921
46922         if(this.loadMask){
46923             this.loadMask = new Roo.LoadMask(this.container,
46924                     Roo.apply({store:this.dataSource}, this.loadMask));
46925         }
46926         
46927         
46928         if (this.toolbar && this.toolbar.xtype) {
46929             this.toolbar.container = this.getView().getHeaderPanel(true);
46930             this.toolbar = new Roo.Toolbar(this.toolbar);
46931         }
46932         if (this.footer && this.footer.xtype) {
46933             this.footer.dataSource = this.getDataSource();
46934             this.footer.container = this.getView().getFooterPanel(true);
46935             this.footer = Roo.factory(this.footer, Roo);
46936         }
46937         if (this.dropTarget && this.dropTarget.xtype) {
46938             delete this.dropTarget.xtype;
46939             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
46940         }
46941         
46942         
46943         this.rendered = true;
46944         this.fireEvent('render', this);
46945         return this;
46946     },
46947
46948         /**
46949          * Reconfigures the grid to use a different Store and Column Model.
46950          * The View will be bound to the new objects and refreshed.
46951          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
46952          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
46953          */
46954     reconfigure : function(dataSource, colModel){
46955         if(this.loadMask){
46956             this.loadMask.destroy();
46957             this.loadMask = new Roo.LoadMask(this.container,
46958                     Roo.apply({store:dataSource}, this.loadMask));
46959         }
46960         this.view.bind(dataSource, colModel);
46961         this.dataSource = dataSource;
46962         this.colModel = colModel;
46963         this.view.refresh(true);
46964     },
46965
46966     // private
46967     onKeyDown : function(e){
46968         this.fireEvent("keydown", e);
46969     },
46970
46971     /**
46972      * Destroy this grid.
46973      * @param {Boolean} removeEl True to remove the element
46974      */
46975     destroy : function(removeEl, keepListeners){
46976         if(this.loadMask){
46977             this.loadMask.destroy();
46978         }
46979         var c = this.container;
46980         c.removeAllListeners();
46981         this.view.destroy();
46982         this.colModel.purgeListeners();
46983         if(!keepListeners){
46984             this.purgeListeners();
46985         }
46986         c.update("");
46987         if(removeEl === true){
46988             c.remove();
46989         }
46990     },
46991
46992     // private
46993     processEvent : function(name, e){
46994         this.fireEvent(name, e);
46995         var t = e.getTarget();
46996         var v = this.view;
46997         var header = v.findHeaderIndex(t);
46998         if(header !== false){
46999             this.fireEvent("header" + name, this, header, e);
47000         }else{
47001             var row = v.findRowIndex(t);
47002             var cell = v.findCellIndex(t);
47003             if(row !== false){
47004                 this.fireEvent("row" + name, this, row, e);
47005                 if(cell !== false){
47006                     this.fireEvent("cell" + name, this, row, cell, e);
47007                 }
47008             }
47009         }
47010     },
47011
47012     // private
47013     onClick : function(e){
47014         this.processEvent("click", e);
47015     },
47016
47017     // private
47018     onContextMenu : function(e, t){
47019         this.processEvent("contextmenu", e);
47020     },
47021
47022     // private
47023     onDblClick : function(e){
47024         this.processEvent("dblclick", e);
47025     },
47026
47027     // private
47028     walkCells : function(row, col, step, fn, scope){
47029         var cm = this.colModel, clen = cm.getColumnCount();
47030         var ds = this.dataSource, rlen = ds.getCount(), first = true;
47031         if(step < 0){
47032             if(col < 0){
47033                 row--;
47034                 first = false;
47035             }
47036             while(row >= 0){
47037                 if(!first){
47038                     col = clen-1;
47039                 }
47040                 first = false;
47041                 while(col >= 0){
47042                     if(fn.call(scope || this, row, col, cm) === true){
47043                         return [row, col];
47044                     }
47045                     col--;
47046                 }
47047                 row--;
47048             }
47049         } else {
47050             if(col >= clen){
47051                 row++;
47052                 first = false;
47053             }
47054             while(row < rlen){
47055                 if(!first){
47056                     col = 0;
47057                 }
47058                 first = false;
47059                 while(col < clen){
47060                     if(fn.call(scope || this, row, col, cm) === true){
47061                         return [row, col];
47062                     }
47063                     col++;
47064                 }
47065                 row++;
47066             }
47067         }
47068         return null;
47069     },
47070
47071     // private
47072     getSelections : function(){
47073         return this.selModel.getSelections();
47074     },
47075
47076     /**
47077      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
47078      * but if manual update is required this method will initiate it.
47079      */
47080     autoSize : function(){
47081         if(this.rendered){
47082             this.view.layout();
47083             if(this.view.adjustForScroll){
47084                 this.view.adjustForScroll();
47085             }
47086         }
47087     },
47088
47089     /**
47090      * Returns the grid's underlying element.
47091      * @return {Element} The element
47092      */
47093     getGridEl : function(){
47094         return this.container;
47095     },
47096
47097     // private for compatibility, overridden by editor grid
47098     stopEditing : function(){},
47099
47100     /**
47101      * Returns the grid's SelectionModel.
47102      * @return {SelectionModel}
47103      */
47104     getSelectionModel : function(){
47105         if(!this.selModel){
47106             this.selModel = new Roo.grid.RowSelectionModel();
47107         }
47108         return this.selModel;
47109     },
47110
47111     /**
47112      * Returns the grid's DataSource.
47113      * @return {DataSource}
47114      */
47115     getDataSource : function(){
47116         return this.dataSource;
47117     },
47118
47119     /**
47120      * Returns the grid's ColumnModel.
47121      * @return {ColumnModel}
47122      */
47123     getColumnModel : function(){
47124         return this.colModel;
47125     },
47126
47127     /**
47128      * Returns the grid's GridView object.
47129      * @return {GridView}
47130      */
47131     getView : function(){
47132         if(!this.view){
47133             this.view = new Roo.grid.GridView(this.viewConfig);
47134         }
47135         return this.view;
47136     },
47137     /**
47138      * Called to get grid's drag proxy text, by default returns this.ddText.
47139      * @return {String}
47140      */
47141     getDragDropText : function(){
47142         var count = this.selModel.getCount();
47143         return String.format(this.ddText, count, count == 1 ? '' : 's');
47144     }
47145 });
47146 /**
47147  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
47148  * %0 is replaced with the number of selected rows.
47149  * @type String
47150  */
47151 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
47152  * Based on:
47153  * Ext JS Library 1.1.1
47154  * Copyright(c) 2006-2007, Ext JS, LLC.
47155  *
47156  * Originally Released Under LGPL - original licence link has changed is not relivant.
47157  *
47158  * Fork - LGPL
47159  * <script type="text/javascript">
47160  */
47161  
47162 Roo.grid.AbstractGridView = function(){
47163         this.grid = null;
47164         
47165         this.events = {
47166             "beforerowremoved" : true,
47167             "beforerowsinserted" : true,
47168             "beforerefresh" : true,
47169             "rowremoved" : true,
47170             "rowsinserted" : true,
47171             "rowupdated" : true,
47172             "refresh" : true
47173         };
47174     Roo.grid.AbstractGridView.superclass.constructor.call(this);
47175 };
47176
47177 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
47178     rowClass : "x-grid-row",
47179     cellClass : "x-grid-cell",
47180     tdClass : "x-grid-td",
47181     hdClass : "x-grid-hd",
47182     splitClass : "x-grid-hd-split",
47183     
47184         init: function(grid){
47185         this.grid = grid;
47186                 var cid = this.grid.getGridEl().id;
47187         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
47188         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
47189         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
47190         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
47191         },
47192         
47193         getColumnRenderers : function(){
47194         var renderers = [];
47195         var cm = this.grid.colModel;
47196         var colCount = cm.getColumnCount();
47197         for(var i = 0; i < colCount; i++){
47198             renderers[i] = cm.getRenderer(i);
47199         }
47200         return renderers;
47201     },
47202     
47203     getColumnIds : function(){
47204         var ids = [];
47205         var cm = this.grid.colModel;
47206         var colCount = cm.getColumnCount();
47207         for(var i = 0; i < colCount; i++){
47208             ids[i] = cm.getColumnId(i);
47209         }
47210         return ids;
47211     },
47212     
47213     getDataIndexes : function(){
47214         if(!this.indexMap){
47215             this.indexMap = this.buildIndexMap();
47216         }
47217         return this.indexMap.colToData;
47218     },
47219     
47220     getColumnIndexByDataIndex : function(dataIndex){
47221         if(!this.indexMap){
47222             this.indexMap = this.buildIndexMap();
47223         }
47224         return this.indexMap.dataToCol[dataIndex];
47225     },
47226     
47227     /**
47228      * Set a css style for a column dynamically. 
47229      * @param {Number} colIndex The index of the column
47230      * @param {String} name The css property name
47231      * @param {String} value The css value
47232      */
47233     setCSSStyle : function(colIndex, name, value){
47234         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
47235         Roo.util.CSS.updateRule(selector, name, value);
47236     },
47237     
47238     generateRules : function(cm){
47239         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
47240         Roo.util.CSS.removeStyleSheet(rulesId);
47241         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47242             var cid = cm.getColumnId(i);
47243             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
47244                          this.tdSelector, cid, " {\n}\n",
47245                          this.hdSelector, cid, " {\n}\n",
47246                          this.splitSelector, cid, " {\n}\n");
47247         }
47248         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
47249     }
47250 });/*
47251  * Based on:
47252  * Ext JS Library 1.1.1
47253  * Copyright(c) 2006-2007, Ext JS, LLC.
47254  *
47255  * Originally Released Under LGPL - original licence link has changed is not relivant.
47256  *
47257  * Fork - LGPL
47258  * <script type="text/javascript">
47259  */
47260
47261 // private
47262 // This is a support class used internally by the Grid components
47263 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
47264     this.grid = grid;
47265     this.view = grid.getView();
47266     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
47267     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
47268     if(hd2){
47269         this.setHandleElId(Roo.id(hd));
47270         this.setOuterHandleElId(Roo.id(hd2));
47271     }
47272     this.scroll = false;
47273 };
47274 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
47275     maxDragWidth: 120,
47276     getDragData : function(e){
47277         var t = Roo.lib.Event.getTarget(e);
47278         var h = this.view.findHeaderCell(t);
47279         if(h){
47280             return {ddel: h.firstChild, header:h};
47281         }
47282         return false;
47283     },
47284
47285     onInitDrag : function(e){
47286         this.view.headersDisabled = true;
47287         var clone = this.dragData.ddel.cloneNode(true);
47288         clone.id = Roo.id();
47289         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
47290         this.proxy.update(clone);
47291         return true;
47292     },
47293
47294     afterValidDrop : function(){
47295         var v = this.view;
47296         setTimeout(function(){
47297             v.headersDisabled = false;
47298         }, 50);
47299     },
47300
47301     afterInvalidDrop : function(){
47302         var v = this.view;
47303         setTimeout(function(){
47304             v.headersDisabled = false;
47305         }, 50);
47306     }
47307 });
47308 /*
47309  * Based on:
47310  * Ext JS Library 1.1.1
47311  * Copyright(c) 2006-2007, Ext JS, LLC.
47312  *
47313  * Originally Released Under LGPL - original licence link has changed is not relivant.
47314  *
47315  * Fork - LGPL
47316  * <script type="text/javascript">
47317  */
47318 // private
47319 // This is a support class used internally by the Grid components
47320 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
47321     this.grid = grid;
47322     this.view = grid.getView();
47323     // split the proxies so they don't interfere with mouse events
47324     this.proxyTop = Roo.DomHelper.append(document.body, {
47325         cls:"col-move-top", html:"&#160;"
47326     }, true);
47327     this.proxyBottom = Roo.DomHelper.append(document.body, {
47328         cls:"col-move-bottom", html:"&#160;"
47329     }, true);
47330     this.proxyTop.hide = this.proxyBottom.hide = function(){
47331         this.setLeftTop(-100,-100);
47332         this.setStyle("visibility", "hidden");
47333     };
47334     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
47335     // temporarily disabled
47336     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
47337     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
47338 };
47339 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
47340     proxyOffsets : [-4, -9],
47341     fly: Roo.Element.fly,
47342
47343     getTargetFromEvent : function(e){
47344         var t = Roo.lib.Event.getTarget(e);
47345         var cindex = this.view.findCellIndex(t);
47346         if(cindex !== false){
47347             return this.view.getHeaderCell(cindex);
47348         }
47349         return null;
47350     },
47351
47352     nextVisible : function(h){
47353         var v = this.view, cm = this.grid.colModel;
47354         h = h.nextSibling;
47355         while(h){
47356             if(!cm.isHidden(v.getCellIndex(h))){
47357                 return h;
47358             }
47359             h = h.nextSibling;
47360         }
47361         return null;
47362     },
47363
47364     prevVisible : function(h){
47365         var v = this.view, cm = this.grid.colModel;
47366         h = h.prevSibling;
47367         while(h){
47368             if(!cm.isHidden(v.getCellIndex(h))){
47369                 return h;
47370             }
47371             h = h.prevSibling;
47372         }
47373         return null;
47374     },
47375
47376     positionIndicator : function(h, n, e){
47377         var x = Roo.lib.Event.getPageX(e);
47378         var r = Roo.lib.Dom.getRegion(n.firstChild);
47379         var px, pt, py = r.top + this.proxyOffsets[1];
47380         if((r.right - x) <= (r.right-r.left)/2){
47381             px = r.right+this.view.borderWidth;
47382             pt = "after";
47383         }else{
47384             px = r.left;
47385             pt = "before";
47386         }
47387         var oldIndex = this.view.getCellIndex(h);
47388         var newIndex = this.view.getCellIndex(n);
47389
47390         if(this.grid.colModel.isFixed(newIndex)){
47391             return false;
47392         }
47393
47394         var locked = this.grid.colModel.isLocked(newIndex);
47395
47396         if(pt == "after"){
47397             newIndex++;
47398         }
47399         if(oldIndex < newIndex){
47400             newIndex--;
47401         }
47402         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
47403             return false;
47404         }
47405         px +=  this.proxyOffsets[0];
47406         this.proxyTop.setLeftTop(px, py);
47407         this.proxyTop.show();
47408         if(!this.bottomOffset){
47409             this.bottomOffset = this.view.mainHd.getHeight();
47410         }
47411         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
47412         this.proxyBottom.show();
47413         return pt;
47414     },
47415
47416     onNodeEnter : function(n, dd, e, data){
47417         if(data.header != n){
47418             this.positionIndicator(data.header, n, e);
47419         }
47420     },
47421
47422     onNodeOver : function(n, dd, e, data){
47423         var result = false;
47424         if(data.header != n){
47425             result = this.positionIndicator(data.header, n, e);
47426         }
47427         if(!result){
47428             this.proxyTop.hide();
47429             this.proxyBottom.hide();
47430         }
47431         return result ? this.dropAllowed : this.dropNotAllowed;
47432     },
47433
47434     onNodeOut : function(n, dd, e, data){
47435         this.proxyTop.hide();
47436         this.proxyBottom.hide();
47437     },
47438
47439     onNodeDrop : function(n, dd, e, data){
47440         var h = data.header;
47441         if(h != n){
47442             var cm = this.grid.colModel;
47443             var x = Roo.lib.Event.getPageX(e);
47444             var r = Roo.lib.Dom.getRegion(n.firstChild);
47445             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
47446             var oldIndex = this.view.getCellIndex(h);
47447             var newIndex = this.view.getCellIndex(n);
47448             var locked = cm.isLocked(newIndex);
47449             if(pt == "after"){
47450                 newIndex++;
47451             }
47452             if(oldIndex < newIndex){
47453                 newIndex--;
47454             }
47455             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
47456                 return false;
47457             }
47458             cm.setLocked(oldIndex, locked, true);
47459             cm.moveColumn(oldIndex, newIndex);
47460             this.grid.fireEvent("columnmove", oldIndex, newIndex);
47461             return true;
47462         }
47463         return false;
47464     }
47465 });
47466 /*
47467  * Based on:
47468  * Ext JS Library 1.1.1
47469  * Copyright(c) 2006-2007, Ext JS, LLC.
47470  *
47471  * Originally Released Under LGPL - original licence link has changed is not relivant.
47472  *
47473  * Fork - LGPL
47474  * <script type="text/javascript">
47475  */
47476   
47477 /**
47478  * @class Roo.grid.GridView
47479  * @extends Roo.util.Observable
47480  *
47481  * @constructor
47482  * @param {Object} config
47483  */
47484 Roo.grid.GridView = function(config){
47485     Roo.grid.GridView.superclass.constructor.call(this);
47486     this.el = null;
47487
47488     Roo.apply(this, config);
47489 };
47490
47491 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
47492
47493     /**
47494      * Override this function to apply custom css classes to rows during rendering
47495      * @param {Record} record The record
47496      * @param {Number} index
47497      * @method getRowClass
47498      */
47499     rowClass : "x-grid-row",
47500
47501     cellClass : "x-grid-col",
47502
47503     tdClass : "x-grid-td",
47504
47505     hdClass : "x-grid-hd",
47506
47507     splitClass : "x-grid-split",
47508
47509     sortClasses : ["sort-asc", "sort-desc"],
47510
47511     enableMoveAnim : false,
47512
47513     hlColor: "C3DAF9",
47514
47515     dh : Roo.DomHelper,
47516
47517     fly : Roo.Element.fly,
47518
47519     css : Roo.util.CSS,
47520
47521     borderWidth: 1,
47522
47523     splitOffset: 3,
47524
47525     scrollIncrement : 22,
47526
47527     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
47528
47529     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
47530
47531     bind : function(ds, cm){
47532         if(this.ds){
47533             this.ds.un("load", this.onLoad, this);
47534             this.ds.un("datachanged", this.onDataChange, this);
47535             this.ds.un("add", this.onAdd, this);
47536             this.ds.un("remove", this.onRemove, this);
47537             this.ds.un("update", this.onUpdate, this);
47538             this.ds.un("clear", this.onClear, this);
47539         }
47540         if(ds){
47541             ds.on("load", this.onLoad, this);
47542             ds.on("datachanged", this.onDataChange, this);
47543             ds.on("add", this.onAdd, this);
47544             ds.on("remove", this.onRemove, this);
47545             ds.on("update", this.onUpdate, this);
47546             ds.on("clear", this.onClear, this);
47547         }
47548         this.ds = ds;
47549
47550         if(this.cm){
47551             this.cm.un("widthchange", this.onColWidthChange, this);
47552             this.cm.un("headerchange", this.onHeaderChange, this);
47553             this.cm.un("hiddenchange", this.onHiddenChange, this);
47554             this.cm.un("columnmoved", this.onColumnMove, this);
47555             this.cm.un("columnlockchange", this.onColumnLock, this);
47556         }
47557         if(cm){
47558             this.generateRules(cm);
47559             cm.on("widthchange", this.onColWidthChange, this);
47560             cm.on("headerchange", this.onHeaderChange, this);
47561             cm.on("hiddenchange", this.onHiddenChange, this);
47562             cm.on("columnmoved", this.onColumnMove, this);
47563             cm.on("columnlockchange", this.onColumnLock, this);
47564         }
47565         this.cm = cm;
47566     },
47567
47568     init: function(grid){
47569         Roo.grid.GridView.superclass.init.call(this, grid);
47570
47571         this.bind(grid.dataSource, grid.colModel);
47572
47573         grid.on("headerclick", this.handleHeaderClick, this);
47574
47575         if(grid.trackMouseOver){
47576             grid.on("mouseover", this.onRowOver, this);
47577             grid.on("mouseout", this.onRowOut, this);
47578         }
47579         grid.cancelTextSelection = function(){};
47580         this.gridId = grid.id;
47581
47582         var tpls = this.templates || {};
47583
47584         if(!tpls.master){
47585             tpls.master = new Roo.Template(
47586                '<div class="x-grid" hidefocus="true">',
47587                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
47588                   '<div class="x-grid-topbar"></div>',
47589                   '<div class="x-grid-scroller"><div></div></div>',
47590                   '<div class="x-grid-locked">',
47591                       '<div class="x-grid-header">{lockedHeader}</div>',
47592                       '<div class="x-grid-body">{lockedBody}</div>',
47593                   "</div>",
47594                   '<div class="x-grid-viewport">',
47595                       '<div class="x-grid-header">{header}</div>',
47596                       '<div class="x-grid-body">{body}</div>',
47597                   "</div>",
47598                   '<div class="x-grid-bottombar"></div>',
47599                  
47600                   '<div class="x-grid-resize-proxy">&#160;</div>',
47601                "</div>"
47602             );
47603             tpls.master.disableformats = true;
47604         }
47605
47606         if(!tpls.header){
47607             tpls.header = new Roo.Template(
47608                '<table border="0" cellspacing="0" cellpadding="0">',
47609                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
47610                "</table>{splits}"
47611             );
47612             tpls.header.disableformats = true;
47613         }
47614         tpls.header.compile();
47615
47616         if(!tpls.hcell){
47617             tpls.hcell = new Roo.Template(
47618                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
47619                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
47620                 "</div></td>"
47621              );
47622              tpls.hcell.disableFormats = true;
47623         }
47624         tpls.hcell.compile();
47625
47626         if(!tpls.hsplit){
47627             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
47628             tpls.hsplit.disableFormats = true;
47629         }
47630         tpls.hsplit.compile();
47631
47632         if(!tpls.body){
47633             tpls.body = new Roo.Template(
47634                '<table border="0" cellspacing="0" cellpadding="0">',
47635                "<tbody>{rows}</tbody>",
47636                "</table>"
47637             );
47638             tpls.body.disableFormats = true;
47639         }
47640         tpls.body.compile();
47641
47642         if(!tpls.row){
47643             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
47644             tpls.row.disableFormats = true;
47645         }
47646         tpls.row.compile();
47647
47648         if(!tpls.cell){
47649             tpls.cell = new Roo.Template(
47650                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
47651                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
47652                 "</td>"
47653             );
47654             tpls.cell.disableFormats = true;
47655         }
47656         tpls.cell.compile();
47657
47658         this.templates = tpls;
47659     },
47660
47661     // remap these for backwards compat
47662     onColWidthChange : function(){
47663         this.updateColumns.apply(this, arguments);
47664     },
47665     onHeaderChange : function(){
47666         this.updateHeaders.apply(this, arguments);
47667     }, 
47668     onHiddenChange : function(){
47669         this.handleHiddenChange.apply(this, arguments);
47670     },
47671     onColumnMove : function(){
47672         this.handleColumnMove.apply(this, arguments);
47673     },
47674     onColumnLock : function(){
47675         this.handleLockChange.apply(this, arguments);
47676     },
47677
47678     onDataChange : function(){
47679         this.refresh();
47680         this.updateHeaderSortState();
47681     },
47682
47683     onClear : function(){
47684         this.refresh();
47685     },
47686
47687     onUpdate : function(ds, record){
47688         this.refreshRow(record);
47689     },
47690
47691     refreshRow : function(record){
47692         var ds = this.ds, index;
47693         if(typeof record == 'number'){
47694             index = record;
47695             record = ds.getAt(index);
47696         }else{
47697             index = ds.indexOf(record);
47698         }
47699         this.insertRows(ds, index, index, true);
47700         this.onRemove(ds, record, index+1, true);
47701         this.syncRowHeights(index, index);
47702         this.layout();
47703         this.fireEvent("rowupdated", this, index, record);
47704     },
47705
47706     onAdd : function(ds, records, index){
47707         this.insertRows(ds, index, index + (records.length-1));
47708     },
47709
47710     onRemove : function(ds, record, index, isUpdate){
47711         if(isUpdate !== true){
47712             this.fireEvent("beforerowremoved", this, index, record);
47713         }
47714         var bt = this.getBodyTable(), lt = this.getLockedTable();
47715         if(bt.rows[index]){
47716             bt.firstChild.removeChild(bt.rows[index]);
47717         }
47718         if(lt.rows[index]){
47719             lt.firstChild.removeChild(lt.rows[index]);
47720         }
47721         if(isUpdate !== true){
47722             this.stripeRows(index);
47723             this.syncRowHeights(index, index);
47724             this.layout();
47725             this.fireEvent("rowremoved", this, index, record);
47726         }
47727     },
47728
47729     onLoad : function(){
47730         this.scrollToTop();
47731     },
47732
47733     /**
47734      * Scrolls the grid to the top
47735      */
47736     scrollToTop : function(){
47737         if(this.scroller){
47738             this.scroller.dom.scrollTop = 0;
47739             this.syncScroll();
47740         }
47741     },
47742
47743     /**
47744      * Gets a panel in the header of the grid that can be used for toolbars etc.
47745      * After modifying the contents of this panel a call to grid.autoSize() may be
47746      * required to register any changes in size.
47747      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
47748      * @return Roo.Element
47749      */
47750     getHeaderPanel : function(doShow){
47751         if(doShow){
47752             this.headerPanel.show();
47753         }
47754         return this.headerPanel;
47755     },
47756
47757     /**
47758      * Gets a panel in the footer of the grid that can be used for toolbars etc.
47759      * After modifying the contents of this panel a call to grid.autoSize() may be
47760      * required to register any changes in size.
47761      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
47762      * @return Roo.Element
47763      */
47764     getFooterPanel : function(doShow){
47765         if(doShow){
47766             this.footerPanel.show();
47767         }
47768         return this.footerPanel;
47769     },
47770
47771     initElements : function(){
47772         var E = Roo.Element;
47773         var el = this.grid.getGridEl().dom.firstChild;
47774         var cs = el.childNodes;
47775
47776         this.el = new E(el);
47777         
47778          this.focusEl = new E(el.firstChild);
47779         this.focusEl.swallowEvent("click", true);
47780         
47781         this.headerPanel = new E(cs[1]);
47782         this.headerPanel.enableDisplayMode("block");
47783
47784         this.scroller = new E(cs[2]);
47785         this.scrollSizer = new E(this.scroller.dom.firstChild);
47786
47787         this.lockedWrap = new E(cs[3]);
47788         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
47789         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
47790
47791         this.mainWrap = new E(cs[4]);
47792         this.mainHd = new E(this.mainWrap.dom.firstChild);
47793         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
47794
47795         this.footerPanel = new E(cs[5]);
47796         this.footerPanel.enableDisplayMode("block");
47797
47798         this.resizeProxy = new E(cs[6]);
47799
47800         this.headerSelector = String.format(
47801            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
47802            this.lockedHd.id, this.mainHd.id
47803         );
47804
47805         this.splitterSelector = String.format(
47806            '#{0} div.x-grid-split, #{1} div.x-grid-split',
47807            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
47808         );
47809     },
47810     idToCssName : function(s)
47811     {
47812         return s.replace(/[^a-z0-9]+/ig, '-');
47813     },
47814
47815     getHeaderCell : function(index){
47816         return Roo.DomQuery.select(this.headerSelector)[index];
47817     },
47818
47819     getHeaderCellMeasure : function(index){
47820         return this.getHeaderCell(index).firstChild;
47821     },
47822
47823     getHeaderCellText : function(index){
47824         return this.getHeaderCell(index).firstChild.firstChild;
47825     },
47826
47827     getLockedTable : function(){
47828         return this.lockedBody.dom.firstChild;
47829     },
47830
47831     getBodyTable : function(){
47832         return this.mainBody.dom.firstChild;
47833     },
47834
47835     getLockedRow : function(index){
47836         return this.getLockedTable().rows[index];
47837     },
47838
47839     getRow : function(index){
47840         return this.getBodyTable().rows[index];
47841     },
47842
47843     getRowComposite : function(index){
47844         if(!this.rowEl){
47845             this.rowEl = new Roo.CompositeElementLite();
47846         }
47847         var els = [], lrow, mrow;
47848         if(lrow = this.getLockedRow(index)){
47849             els.push(lrow);
47850         }
47851         if(mrow = this.getRow(index)){
47852             els.push(mrow);
47853         }
47854         this.rowEl.elements = els;
47855         return this.rowEl;
47856     },
47857
47858     getCell : function(rowIndex, colIndex){
47859         var locked = this.cm.getLockedCount();
47860         var source;
47861         if(colIndex < locked){
47862             source = this.lockedBody.dom.firstChild;
47863         }else{
47864             source = this.mainBody.dom.firstChild;
47865             colIndex -= locked;
47866         }
47867         return source.rows[rowIndex].childNodes[colIndex];
47868     },
47869
47870     getCellText : function(rowIndex, colIndex){
47871         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
47872     },
47873
47874     getCellBox : function(cell){
47875         var b = this.fly(cell).getBox();
47876         if(Roo.isOpera){ // opera fails to report the Y
47877             b.y = cell.offsetTop + this.mainBody.getY();
47878         }
47879         return b;
47880     },
47881
47882     getCellIndex : function(cell){
47883         var id = String(cell.className).match(this.cellRE);
47884         if(id){
47885             return parseInt(id[1], 10);
47886         }
47887         return 0;
47888     },
47889
47890     findHeaderIndex : function(n){
47891         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47892         return r ? this.getCellIndex(r) : false;
47893     },
47894
47895     findHeaderCell : function(n){
47896         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47897         return r ? r : false;
47898     },
47899
47900     findRowIndex : function(n){
47901         if(!n){
47902             return false;
47903         }
47904         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
47905         return r ? r.rowIndex : false;
47906     },
47907
47908     findCellIndex : function(node){
47909         var stop = this.el.dom;
47910         while(node && node != stop){
47911             if(this.findRE.test(node.className)){
47912                 return this.getCellIndex(node);
47913             }
47914             node = node.parentNode;
47915         }
47916         return false;
47917     },
47918
47919     getColumnId : function(index){
47920         return this.cm.getColumnId(index);
47921     },
47922
47923     getSplitters : function()
47924     {
47925         if(this.splitterSelector){
47926            return Roo.DomQuery.select(this.splitterSelector);
47927         }else{
47928             return null;
47929       }
47930     },
47931
47932     getSplitter : function(index){
47933         return this.getSplitters()[index];
47934     },
47935
47936     onRowOver : function(e, t){
47937         var row;
47938         if((row = this.findRowIndex(t)) !== false){
47939             this.getRowComposite(row).addClass("x-grid-row-over");
47940         }
47941     },
47942
47943     onRowOut : function(e, t){
47944         var row;
47945         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
47946             this.getRowComposite(row).removeClass("x-grid-row-over");
47947         }
47948     },
47949
47950     renderHeaders : function(){
47951         var cm = this.cm;
47952         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
47953         var cb = [], lb = [], sb = [], lsb = [], p = {};
47954         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47955             p.cellId = "x-grid-hd-0-" + i;
47956             p.splitId = "x-grid-csplit-0-" + i;
47957             p.id = cm.getColumnId(i);
47958             p.title = cm.getColumnTooltip(i) || "";
47959             p.value = cm.getColumnHeader(i) || "";
47960             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
47961             if(!cm.isLocked(i)){
47962                 cb[cb.length] = ct.apply(p);
47963                 sb[sb.length] = st.apply(p);
47964             }else{
47965                 lb[lb.length] = ct.apply(p);
47966                 lsb[lsb.length] = st.apply(p);
47967             }
47968         }
47969         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
47970                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
47971     },
47972
47973     updateHeaders : function(){
47974         var html = this.renderHeaders();
47975         this.lockedHd.update(html[0]);
47976         this.mainHd.update(html[1]);
47977     },
47978
47979     /**
47980      * Focuses the specified row.
47981      * @param {Number} row The row index
47982      */
47983     focusRow : function(row)
47984     {
47985         //Roo.log('GridView.focusRow');
47986         var x = this.scroller.dom.scrollLeft;
47987         this.focusCell(row, 0, false);
47988         this.scroller.dom.scrollLeft = x;
47989     },
47990
47991     /**
47992      * Focuses the specified cell.
47993      * @param {Number} row The row index
47994      * @param {Number} col The column index
47995      * @param {Boolean} hscroll false to disable horizontal scrolling
47996      */
47997     focusCell : function(row, col, hscroll)
47998     {
47999         //Roo.log('GridView.focusCell');
48000         var el = this.ensureVisible(row, col, hscroll);
48001         this.focusEl.alignTo(el, "tl-tl");
48002         if(Roo.isGecko){
48003             this.focusEl.focus();
48004         }else{
48005             this.focusEl.focus.defer(1, this.focusEl);
48006         }
48007     },
48008
48009     /**
48010      * Scrolls the specified cell into view
48011      * @param {Number} row The row index
48012      * @param {Number} col The column index
48013      * @param {Boolean} hscroll false to disable horizontal scrolling
48014      */
48015     ensureVisible : function(row, col, hscroll)
48016     {
48017         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
48018         //return null; //disable for testing.
48019         if(typeof row != "number"){
48020             row = row.rowIndex;
48021         }
48022         if(row < 0 && row >= this.ds.getCount()){
48023             return  null;
48024         }
48025         col = (col !== undefined ? col : 0);
48026         var cm = this.grid.colModel;
48027         while(cm.isHidden(col)){
48028             col++;
48029         }
48030
48031         var el = this.getCell(row, col);
48032         if(!el){
48033             return null;
48034         }
48035         var c = this.scroller.dom;
48036
48037         var ctop = parseInt(el.offsetTop, 10);
48038         var cleft = parseInt(el.offsetLeft, 10);
48039         var cbot = ctop + el.offsetHeight;
48040         var cright = cleft + el.offsetWidth;
48041         
48042         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
48043         var stop = parseInt(c.scrollTop, 10);
48044         var sleft = parseInt(c.scrollLeft, 10);
48045         var sbot = stop + ch;
48046         var sright = sleft + c.clientWidth;
48047         /*
48048         Roo.log('GridView.ensureVisible:' +
48049                 ' ctop:' + ctop +
48050                 ' c.clientHeight:' + c.clientHeight +
48051                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
48052                 ' stop:' + stop +
48053                 ' cbot:' + cbot +
48054                 ' sbot:' + sbot +
48055                 ' ch:' + ch  
48056                 );
48057         */
48058         if(ctop < stop){
48059              c.scrollTop = ctop;
48060             //Roo.log("set scrolltop to ctop DISABLE?");
48061         }else if(cbot > sbot){
48062             //Roo.log("set scrolltop to cbot-ch");
48063             c.scrollTop = cbot-ch;
48064         }
48065         
48066         if(hscroll !== false){
48067             if(cleft < sleft){
48068                 c.scrollLeft = cleft;
48069             }else if(cright > sright){
48070                 c.scrollLeft = cright-c.clientWidth;
48071             }
48072         }
48073          
48074         return el;
48075     },
48076
48077     updateColumns : function(){
48078         this.grid.stopEditing();
48079         var cm = this.grid.colModel, colIds = this.getColumnIds();
48080         //var totalWidth = cm.getTotalWidth();
48081         var pos = 0;
48082         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48083             //if(cm.isHidden(i)) continue;
48084             var w = cm.getColumnWidth(i);
48085             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48086             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48087         }
48088         this.updateSplitters();
48089     },
48090
48091     generateRules : function(cm){
48092         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
48093         Roo.util.CSS.removeStyleSheet(rulesId);
48094         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48095             var cid = cm.getColumnId(i);
48096             var align = '';
48097             if(cm.config[i].align){
48098                 align = 'text-align:'+cm.config[i].align+';';
48099             }
48100             var hidden = '';
48101             if(cm.isHidden(i)){
48102                 hidden = 'display:none;';
48103             }
48104             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
48105             ruleBuf.push(
48106                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
48107                     this.hdSelector, cid, " {\n", align, width, "}\n",
48108                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
48109                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
48110         }
48111         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48112     },
48113
48114     updateSplitters : function(){
48115         var cm = this.cm, s = this.getSplitters();
48116         if(s){ // splitters not created yet
48117             var pos = 0, locked = true;
48118             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48119                 if(cm.isHidden(i)) continue;
48120                 var w = cm.getColumnWidth(i); // make sure it's a number
48121                 if(!cm.isLocked(i) && locked){
48122                     pos = 0;
48123                     locked = false;
48124                 }
48125                 pos += w;
48126                 s[i].style.left = (pos-this.splitOffset) + "px";
48127             }
48128         }
48129     },
48130
48131     handleHiddenChange : function(colModel, colIndex, hidden){
48132         if(hidden){
48133             this.hideColumn(colIndex);
48134         }else{
48135             this.unhideColumn(colIndex);
48136         }
48137     },
48138
48139     hideColumn : function(colIndex){
48140         var cid = this.getColumnId(colIndex);
48141         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
48142         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
48143         if(Roo.isSafari){
48144             this.updateHeaders();
48145         }
48146         this.updateSplitters();
48147         this.layout();
48148     },
48149
48150     unhideColumn : function(colIndex){
48151         var cid = this.getColumnId(colIndex);
48152         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
48153         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
48154
48155         if(Roo.isSafari){
48156             this.updateHeaders();
48157         }
48158         this.updateSplitters();
48159         this.layout();
48160     },
48161
48162     insertRows : function(dm, firstRow, lastRow, isUpdate){
48163         if(firstRow == 0 && lastRow == dm.getCount()-1){
48164             this.refresh();
48165         }else{
48166             if(!isUpdate){
48167                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
48168             }
48169             var s = this.getScrollState();
48170             var markup = this.renderRows(firstRow, lastRow);
48171             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
48172             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
48173             this.restoreScroll(s);
48174             if(!isUpdate){
48175                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
48176                 this.syncRowHeights(firstRow, lastRow);
48177                 this.stripeRows(firstRow);
48178                 this.layout();
48179             }
48180         }
48181     },
48182
48183     bufferRows : function(markup, target, index){
48184         var before = null, trows = target.rows, tbody = target.tBodies[0];
48185         if(index < trows.length){
48186             before = trows[index];
48187         }
48188         var b = document.createElement("div");
48189         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
48190         var rows = b.firstChild.rows;
48191         for(var i = 0, len = rows.length; i < len; i++){
48192             if(before){
48193                 tbody.insertBefore(rows[0], before);
48194             }else{
48195                 tbody.appendChild(rows[0]);
48196             }
48197         }
48198         b.innerHTML = "";
48199         b = null;
48200     },
48201
48202     deleteRows : function(dm, firstRow, lastRow){
48203         if(dm.getRowCount()<1){
48204             this.fireEvent("beforerefresh", this);
48205             this.mainBody.update("");
48206             this.lockedBody.update("");
48207             this.fireEvent("refresh", this);
48208         }else{
48209             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
48210             var bt = this.getBodyTable();
48211             var tbody = bt.firstChild;
48212             var rows = bt.rows;
48213             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
48214                 tbody.removeChild(rows[firstRow]);
48215             }
48216             this.stripeRows(firstRow);
48217             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
48218         }
48219     },
48220
48221     updateRows : function(dataSource, firstRow, lastRow){
48222         var s = this.getScrollState();
48223         this.refresh();
48224         this.restoreScroll(s);
48225     },
48226
48227     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
48228         if(!noRefresh){
48229            this.refresh();
48230         }
48231         this.updateHeaderSortState();
48232     },
48233
48234     getScrollState : function(){
48235         
48236         var sb = this.scroller.dom;
48237         return {left: sb.scrollLeft, top: sb.scrollTop};
48238     },
48239
48240     stripeRows : function(startRow){
48241         if(!this.grid.stripeRows || this.ds.getCount() < 1){
48242             return;
48243         }
48244         startRow = startRow || 0;
48245         var rows = this.getBodyTable().rows;
48246         var lrows = this.getLockedTable().rows;
48247         var cls = ' x-grid-row-alt ';
48248         for(var i = startRow, len = rows.length; i < len; i++){
48249             var row = rows[i], lrow = lrows[i];
48250             var isAlt = ((i+1) % 2 == 0);
48251             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
48252             if(isAlt == hasAlt){
48253                 continue;
48254             }
48255             if(isAlt){
48256                 row.className += " x-grid-row-alt";
48257             }else{
48258                 row.className = row.className.replace("x-grid-row-alt", "");
48259             }
48260             if(lrow){
48261                 lrow.className = row.className;
48262             }
48263         }
48264     },
48265
48266     restoreScroll : function(state){
48267         //Roo.log('GridView.restoreScroll');
48268         var sb = this.scroller.dom;
48269         sb.scrollLeft = state.left;
48270         sb.scrollTop = state.top;
48271         this.syncScroll();
48272     },
48273
48274     syncScroll : function(){
48275         //Roo.log('GridView.syncScroll');
48276         var sb = this.scroller.dom;
48277         var sh = this.mainHd.dom;
48278         var bs = this.mainBody.dom;
48279         var lv = this.lockedBody.dom;
48280         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
48281         lv.scrollTop = bs.scrollTop = sb.scrollTop;
48282     },
48283
48284     handleScroll : function(e){
48285         this.syncScroll();
48286         var sb = this.scroller.dom;
48287         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
48288         e.stopEvent();
48289     },
48290
48291     handleWheel : function(e){
48292         var d = e.getWheelDelta();
48293         this.scroller.dom.scrollTop -= d*22;
48294         // set this here to prevent jumpy scrolling on large tables
48295         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
48296         e.stopEvent();
48297     },
48298
48299     renderRows : function(startRow, endRow){
48300         // pull in all the crap needed to render rows
48301         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
48302         var colCount = cm.getColumnCount();
48303
48304         if(ds.getCount() < 1){
48305             return ["", ""];
48306         }
48307
48308         // build a map for all the columns
48309         var cs = [];
48310         for(var i = 0; i < colCount; i++){
48311             var name = cm.getDataIndex(i);
48312             cs[i] = {
48313                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
48314                 renderer : cm.getRenderer(i),
48315                 id : cm.getColumnId(i),
48316                 locked : cm.isLocked(i)
48317             };
48318         }
48319
48320         startRow = startRow || 0;
48321         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
48322
48323         // records to render
48324         var rs = ds.getRange(startRow, endRow);
48325
48326         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
48327     },
48328
48329     // As much as I hate to duplicate code, this was branched because FireFox really hates
48330     // [].join("") on strings. The performance difference was substantial enough to
48331     // branch this function
48332     doRender : Roo.isGecko ?
48333             function(cs, rs, ds, startRow, colCount, stripe){
48334                 var ts = this.templates, ct = ts.cell, rt = ts.row;
48335                 // buffers
48336                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
48337                 
48338                 var hasListener = this.grid.hasListener('rowclass');
48339                 var rowcfg = {};
48340                 for(var j = 0, len = rs.length; j < len; j++){
48341                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
48342                     for(var i = 0; i < colCount; i++){
48343                         c = cs[i];
48344                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
48345                         p.id = c.id;
48346                         p.css = p.attr = "";
48347                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
48348                         if(p.value == undefined || p.value === "") p.value = "&#160;";
48349                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
48350                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
48351                         }
48352                         var markup = ct.apply(p);
48353                         if(!c.locked){
48354                             cb+= markup;
48355                         }else{
48356                             lcb+= markup;
48357                         }
48358                     }
48359                     var alt = [];
48360                     if(stripe && ((rowIndex+1) % 2 == 0)){
48361                         alt.push("x-grid-row-alt")
48362                     }
48363                     if(r.dirty){
48364                         alt.push(  " x-grid-dirty-row");
48365                     }
48366                     rp.cells = lcb;
48367                     if(this.getRowClass){
48368                         alt.push(this.getRowClass(r, rowIndex));
48369                     }
48370                     if (hasListener) {
48371                         rowcfg = {
48372                              
48373                             record: r,
48374                             rowIndex : rowIndex,
48375                             rowClass : ''
48376                         }
48377                         this.grid.fireEvent('rowclass', this, rowcfg);
48378                         alt.push(rowcfg.rowClass);
48379                     }
48380                     rp.alt = alt.join(" ");
48381                     lbuf+= rt.apply(rp);
48382                     rp.cells = cb;
48383                     buf+=  rt.apply(rp);
48384                 }
48385                 return [lbuf, buf];
48386             } :
48387             function(cs, rs, ds, startRow, colCount, stripe){
48388                 var ts = this.templates, ct = ts.cell, rt = ts.row;
48389                 // buffers
48390                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
48391                 var hasListener = this.grid.hasListener('rowclass');
48392                 var rowcfg = {};
48393                 for(var j = 0, len = rs.length; j < len; j++){
48394                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
48395                     for(var i = 0; i < colCount; i++){
48396                         c = cs[i];
48397                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
48398                         p.id = c.id;
48399                         p.css = p.attr = "";
48400                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
48401                         if(p.value == undefined || p.value === "") p.value = "&#160;";
48402                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
48403                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
48404                         }
48405                         var markup = ct.apply(p);
48406                         if(!c.locked){
48407                             cb[cb.length] = markup;
48408                         }else{
48409                             lcb[lcb.length] = markup;
48410                         }
48411                     }
48412                     var alt = [];
48413                     if(stripe && ((rowIndex+1) % 2 == 0)){
48414                         alt.push( "x-grid-row-alt");
48415                     }
48416                     if(r.dirty){
48417                         alt.push(" x-grid-dirty-row");
48418                     }
48419                     rp.cells = lcb;
48420                     if(this.getRowClass){
48421                         alt.push( this.getRowClass(r, rowIndex));
48422                     }
48423                     if (hasListener) {
48424                         rowcfg = {
48425                              
48426                             record: r,
48427                             rowIndex : rowIndex,
48428                             rowClass : ''
48429                         }
48430                         this.grid.fireEvent('rowclass', this, rowcfg);
48431                         alt.push(rowcfg.rowClass);
48432                     }
48433                     rp.alt = alt.join(" ");
48434                     rp.cells = lcb.join("");
48435                     lbuf[lbuf.length] = rt.apply(rp);
48436                     rp.cells = cb.join("");
48437                     buf[buf.length] =  rt.apply(rp);
48438                 }
48439                 return [lbuf.join(""), buf.join("")];
48440             },
48441
48442     renderBody : function(){
48443         var markup = this.renderRows();
48444         var bt = this.templates.body;
48445         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
48446     },
48447
48448     /**
48449      * Refreshes the grid
48450      * @param {Boolean} headersToo
48451      */
48452     refresh : function(headersToo){
48453         this.fireEvent("beforerefresh", this);
48454         this.grid.stopEditing();
48455         var result = this.renderBody();
48456         this.lockedBody.update(result[0]);
48457         this.mainBody.update(result[1]);
48458         if(headersToo === true){
48459             this.updateHeaders();
48460             this.updateColumns();
48461             this.updateSplitters();
48462             this.updateHeaderSortState();
48463         }
48464         this.syncRowHeights();
48465         this.layout();
48466         this.fireEvent("refresh", this);
48467     },
48468
48469     handleColumnMove : function(cm, oldIndex, newIndex){
48470         this.indexMap = null;
48471         var s = this.getScrollState();
48472         this.refresh(true);
48473         this.restoreScroll(s);
48474         this.afterMove(newIndex);
48475     },
48476
48477     afterMove : function(colIndex){
48478         if(this.enableMoveAnim && Roo.enableFx){
48479             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
48480         }
48481         // if multisort - fix sortOrder, and reload..
48482         if (this.grid.dataSource.multiSort) {
48483             // the we can call sort again..
48484             var dm = this.grid.dataSource;
48485             var cm = this.grid.colModel;
48486             var so = [];
48487             for(var i = 0; i < cm.config.length; i++ ) {
48488                 
48489                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
48490                     continue; // dont' bother, it's not in sort list or being set.
48491                 }
48492                 
48493                 so.push(cm.config[i].dataIndex);
48494             };
48495             dm.sortOrder = so;
48496             dm.load(dm.lastOptions);
48497             
48498             
48499         }
48500         
48501     },
48502
48503     updateCell : function(dm, rowIndex, dataIndex){
48504         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
48505         if(typeof colIndex == "undefined"){ // not present in grid
48506             return;
48507         }
48508         var cm = this.grid.colModel;
48509         var cell = this.getCell(rowIndex, colIndex);
48510         var cellText = this.getCellText(rowIndex, colIndex);
48511
48512         var p = {
48513             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
48514             id : cm.getColumnId(colIndex),
48515             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
48516         };
48517         var renderer = cm.getRenderer(colIndex);
48518         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
48519         if(typeof val == "undefined" || val === "") val = "&#160;";
48520         cellText.innerHTML = val;
48521         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
48522         this.syncRowHeights(rowIndex, rowIndex);
48523     },
48524
48525     calcColumnWidth : function(colIndex, maxRowsToMeasure){
48526         var maxWidth = 0;
48527         if(this.grid.autoSizeHeaders){
48528             var h = this.getHeaderCellMeasure(colIndex);
48529             maxWidth = Math.max(maxWidth, h.scrollWidth);
48530         }
48531         var tb, index;
48532         if(this.cm.isLocked(colIndex)){
48533             tb = this.getLockedTable();
48534             index = colIndex;
48535         }else{
48536             tb = this.getBodyTable();
48537             index = colIndex - this.cm.getLockedCount();
48538         }
48539         if(tb && tb.rows){
48540             var rows = tb.rows;
48541             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
48542             for(var i = 0; i < stopIndex; i++){
48543                 var cell = rows[i].childNodes[index].firstChild;
48544                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
48545             }
48546         }
48547         return maxWidth + /*margin for error in IE*/ 5;
48548     },
48549     /**
48550      * Autofit a column to its content.
48551      * @param {Number} colIndex
48552      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
48553      */
48554      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
48555          if(this.cm.isHidden(colIndex)){
48556              return; // can't calc a hidden column
48557          }
48558         if(forceMinSize){
48559             var cid = this.cm.getColumnId(colIndex);
48560             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
48561            if(this.grid.autoSizeHeaders){
48562                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
48563            }
48564         }
48565         var newWidth = this.calcColumnWidth(colIndex);
48566         this.cm.setColumnWidth(colIndex,
48567             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
48568         if(!suppressEvent){
48569             this.grid.fireEvent("columnresize", colIndex, newWidth);
48570         }
48571     },
48572
48573     /**
48574      * Autofits all columns to their content and then expands to fit any extra space in the grid
48575      */
48576      autoSizeColumns : function(){
48577         var cm = this.grid.colModel;
48578         var colCount = cm.getColumnCount();
48579         for(var i = 0; i < colCount; i++){
48580             this.autoSizeColumn(i, true, true);
48581         }
48582         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
48583             this.fitColumns();
48584         }else{
48585             this.updateColumns();
48586             this.layout();
48587         }
48588     },
48589
48590     /**
48591      * Autofits all columns to the grid's width proportionate with their current size
48592      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
48593      */
48594     fitColumns : function(reserveScrollSpace){
48595         var cm = this.grid.colModel;
48596         var colCount = cm.getColumnCount();
48597         var cols = [];
48598         var width = 0;
48599         var i, w;
48600         for (i = 0; i < colCount; i++){
48601             if(!cm.isHidden(i) && !cm.isFixed(i)){
48602                 w = cm.getColumnWidth(i);
48603                 cols.push(i);
48604                 cols.push(w);
48605                 width += w;
48606             }
48607         }
48608         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
48609         if(reserveScrollSpace){
48610             avail -= 17;
48611         }
48612         var frac = (avail - cm.getTotalWidth())/width;
48613         while (cols.length){
48614             w = cols.pop();
48615             i = cols.pop();
48616             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
48617         }
48618         this.updateColumns();
48619         this.layout();
48620     },
48621
48622     onRowSelect : function(rowIndex){
48623         var row = this.getRowComposite(rowIndex);
48624         row.addClass("x-grid-row-selected");
48625     },
48626
48627     onRowDeselect : function(rowIndex){
48628         var row = this.getRowComposite(rowIndex);
48629         row.removeClass("x-grid-row-selected");
48630     },
48631
48632     onCellSelect : function(row, col){
48633         var cell = this.getCell(row, col);
48634         if(cell){
48635             Roo.fly(cell).addClass("x-grid-cell-selected");
48636         }
48637     },
48638
48639     onCellDeselect : function(row, col){
48640         var cell = this.getCell(row, col);
48641         if(cell){
48642             Roo.fly(cell).removeClass("x-grid-cell-selected");
48643         }
48644     },
48645
48646     updateHeaderSortState : function(){
48647         
48648         // sort state can be single { field: xxx, direction : yyy}
48649         // or   { xxx=>ASC , yyy : DESC ..... }
48650         
48651         var mstate = {};
48652         if (!this.ds.multiSort) { 
48653             var state = this.ds.getSortState();
48654             if(!state){
48655                 return;
48656             }
48657             mstate[state.field] = state.direction;
48658             // FIXME... - this is not used here.. but might be elsewhere..
48659             this.sortState = state;
48660             
48661         } else {
48662             mstate = this.ds.sortToggle;
48663         }
48664         //remove existing sort classes..
48665         
48666         var sc = this.sortClasses;
48667         var hds = this.el.select(this.headerSelector).removeClass(sc);
48668         
48669         for(var f in mstate) {
48670         
48671             var sortColumn = this.cm.findColumnIndex(f);
48672             
48673             if(sortColumn != -1){
48674                 var sortDir = mstate[f];        
48675                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
48676             }
48677         }
48678         
48679          
48680         
48681     },
48682
48683
48684     handleHeaderClick : function(g, index){
48685         if(this.headersDisabled){
48686             return;
48687         }
48688         var dm = g.dataSource, cm = g.colModel;
48689         if(!cm.isSortable(index)){
48690             return;
48691         }
48692         g.stopEditing();
48693         
48694         if (dm.multiSort) {
48695             // update the sortOrder
48696             var so = [];
48697             for(var i = 0; i < cm.config.length; i++ ) {
48698                 
48699                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
48700                     continue; // dont' bother, it's not in sort list or being set.
48701                 }
48702                 
48703                 so.push(cm.config[i].dataIndex);
48704             };
48705             dm.sortOrder = so;
48706         }
48707         
48708         
48709         dm.sort(cm.getDataIndex(index));
48710     },
48711
48712
48713     destroy : function(){
48714         if(this.colMenu){
48715             this.colMenu.removeAll();
48716             Roo.menu.MenuMgr.unregister(this.colMenu);
48717             this.colMenu.getEl().remove();
48718             delete this.colMenu;
48719         }
48720         if(this.hmenu){
48721             this.hmenu.removeAll();
48722             Roo.menu.MenuMgr.unregister(this.hmenu);
48723             this.hmenu.getEl().remove();
48724             delete this.hmenu;
48725         }
48726         if(this.grid.enableColumnMove){
48727             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48728             if(dds){
48729                 for(var dd in dds){
48730                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
48731                         var elid = dds[dd].dragElId;
48732                         dds[dd].unreg();
48733                         Roo.get(elid).remove();
48734                     } else if(dds[dd].config.isTarget){
48735                         dds[dd].proxyTop.remove();
48736                         dds[dd].proxyBottom.remove();
48737                         dds[dd].unreg();
48738                     }
48739                     if(Roo.dd.DDM.locationCache[dd]){
48740                         delete Roo.dd.DDM.locationCache[dd];
48741                     }
48742                 }
48743                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48744             }
48745         }
48746         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
48747         this.bind(null, null);
48748         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
48749     },
48750
48751     handleLockChange : function(){
48752         this.refresh(true);
48753     },
48754
48755     onDenyColumnLock : function(){
48756
48757     },
48758
48759     onDenyColumnHide : function(){
48760
48761     },
48762
48763     handleHdMenuClick : function(item){
48764         var index = this.hdCtxIndex;
48765         var cm = this.cm, ds = this.ds;
48766         switch(item.id){
48767             case "asc":
48768                 ds.sort(cm.getDataIndex(index), "ASC");
48769                 break;
48770             case "desc":
48771                 ds.sort(cm.getDataIndex(index), "DESC");
48772                 break;
48773             case "lock":
48774                 var lc = cm.getLockedCount();
48775                 if(cm.getColumnCount(true) <= lc+1){
48776                     this.onDenyColumnLock();
48777                     return;
48778                 }
48779                 if(lc != index){
48780                     cm.setLocked(index, true, true);
48781                     cm.moveColumn(index, lc);
48782                     this.grid.fireEvent("columnmove", index, lc);
48783                 }else{
48784                     cm.setLocked(index, true);
48785                 }
48786             break;
48787             case "unlock":
48788                 var lc = cm.getLockedCount();
48789                 if((lc-1) != index){
48790                     cm.setLocked(index, false, true);
48791                     cm.moveColumn(index, lc-1);
48792                     this.grid.fireEvent("columnmove", index, lc-1);
48793                 }else{
48794                     cm.setLocked(index, false);
48795                 }
48796             break;
48797             default:
48798                 index = cm.getIndexById(item.id.substr(4));
48799                 if(index != -1){
48800                     if(item.checked && cm.getColumnCount(true) <= 1){
48801                         this.onDenyColumnHide();
48802                         return false;
48803                     }
48804                     cm.setHidden(index, item.checked);
48805                 }
48806         }
48807         return true;
48808     },
48809
48810     beforeColMenuShow : function(){
48811         var cm = this.cm,  colCount = cm.getColumnCount();
48812         this.colMenu.removeAll();
48813         for(var i = 0; i < colCount; i++){
48814             this.colMenu.add(new Roo.menu.CheckItem({
48815                 id: "col-"+cm.getColumnId(i),
48816                 text: cm.getColumnHeader(i),
48817                 checked: !cm.isHidden(i),
48818                 hideOnClick:false
48819             }));
48820         }
48821     },
48822
48823     handleHdCtx : function(g, index, e){
48824         e.stopEvent();
48825         var hd = this.getHeaderCell(index);
48826         this.hdCtxIndex = index;
48827         var ms = this.hmenu.items, cm = this.cm;
48828         ms.get("asc").setDisabled(!cm.isSortable(index));
48829         ms.get("desc").setDisabled(!cm.isSortable(index));
48830         if(this.grid.enableColLock !== false){
48831             ms.get("lock").setDisabled(cm.isLocked(index));
48832             ms.get("unlock").setDisabled(!cm.isLocked(index));
48833         }
48834         this.hmenu.show(hd, "tl-bl");
48835     },
48836
48837     handleHdOver : function(e){
48838         var hd = this.findHeaderCell(e.getTarget());
48839         if(hd && !this.headersDisabled){
48840             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
48841                this.fly(hd).addClass("x-grid-hd-over");
48842             }
48843         }
48844     },
48845
48846     handleHdOut : function(e){
48847         var hd = this.findHeaderCell(e.getTarget());
48848         if(hd){
48849             this.fly(hd).removeClass("x-grid-hd-over");
48850         }
48851     },
48852
48853     handleSplitDblClick : function(e, t){
48854         var i = this.getCellIndex(t);
48855         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
48856             this.autoSizeColumn(i, true);
48857             this.layout();
48858         }
48859     },
48860
48861     render : function(){
48862
48863         var cm = this.cm;
48864         var colCount = cm.getColumnCount();
48865
48866         if(this.grid.monitorWindowResize === true){
48867             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48868         }
48869         var header = this.renderHeaders();
48870         var body = this.templates.body.apply({rows:""});
48871         var html = this.templates.master.apply({
48872             lockedBody: body,
48873             body: body,
48874             lockedHeader: header[0],
48875             header: header[1]
48876         });
48877
48878         //this.updateColumns();
48879
48880         this.grid.getGridEl().dom.innerHTML = html;
48881
48882         this.initElements();
48883         
48884         // a kludge to fix the random scolling effect in webkit
48885         this.el.on("scroll", function() {
48886             this.el.dom.scrollTop=0; // hopefully not recursive..
48887         },this);
48888
48889         this.scroller.on("scroll", this.handleScroll, this);
48890         this.lockedBody.on("mousewheel", this.handleWheel, this);
48891         this.mainBody.on("mousewheel", this.handleWheel, this);
48892
48893         this.mainHd.on("mouseover", this.handleHdOver, this);
48894         this.mainHd.on("mouseout", this.handleHdOut, this);
48895         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
48896                 {delegate: "."+this.splitClass});
48897
48898         this.lockedHd.on("mouseover", this.handleHdOver, this);
48899         this.lockedHd.on("mouseout", this.handleHdOut, this);
48900         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
48901                 {delegate: "."+this.splitClass});
48902
48903         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
48904             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48905         }
48906
48907         this.updateSplitters();
48908
48909         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
48910             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48911             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48912         }
48913
48914         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
48915             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
48916             this.hmenu.add(
48917                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
48918                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
48919             );
48920             if(this.grid.enableColLock !== false){
48921                 this.hmenu.add('-',
48922                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
48923                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
48924                 );
48925             }
48926             if(this.grid.enableColumnHide !== false){
48927
48928                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
48929                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
48930                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
48931
48932                 this.hmenu.add('-',
48933                     {id:"columns", text: this.columnsText, menu: this.colMenu}
48934                 );
48935             }
48936             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
48937
48938             this.grid.on("headercontextmenu", this.handleHdCtx, this);
48939         }
48940
48941         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
48942             this.dd = new Roo.grid.GridDragZone(this.grid, {
48943                 ddGroup : this.grid.ddGroup || 'GridDD'
48944             });
48945         }
48946
48947         /*
48948         for(var i = 0; i < colCount; i++){
48949             if(cm.isHidden(i)){
48950                 this.hideColumn(i);
48951             }
48952             if(cm.config[i].align){
48953                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
48954                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
48955             }
48956         }*/
48957         
48958         this.updateHeaderSortState();
48959
48960         this.beforeInitialResize();
48961         this.layout(true);
48962
48963         // two part rendering gives faster view to the user
48964         this.renderPhase2.defer(1, this);
48965     },
48966
48967     renderPhase2 : function(){
48968         // render the rows now
48969         this.refresh();
48970         if(this.grid.autoSizeColumns){
48971             this.autoSizeColumns();
48972         }
48973     },
48974
48975     beforeInitialResize : function(){
48976
48977     },
48978
48979     onColumnSplitterMoved : function(i, w){
48980         this.userResized = true;
48981         var cm = this.grid.colModel;
48982         cm.setColumnWidth(i, w, true);
48983         var cid = cm.getColumnId(i);
48984         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48985         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48986         this.updateSplitters();
48987         this.layout();
48988         this.grid.fireEvent("columnresize", i, w);
48989     },
48990
48991     syncRowHeights : function(startIndex, endIndex){
48992         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
48993             startIndex = startIndex || 0;
48994             var mrows = this.getBodyTable().rows;
48995             var lrows = this.getLockedTable().rows;
48996             var len = mrows.length-1;
48997             endIndex = Math.min(endIndex || len, len);
48998             for(var i = startIndex; i <= endIndex; i++){
48999                 var m = mrows[i], l = lrows[i];
49000                 var h = Math.max(m.offsetHeight, l.offsetHeight);
49001                 m.style.height = l.style.height = h + "px";
49002             }
49003         }
49004     },
49005
49006     layout : function(initialRender, is2ndPass){
49007         var g = this.grid;
49008         var auto = g.autoHeight;
49009         var scrollOffset = 16;
49010         var c = g.getGridEl(), cm = this.cm,
49011                 expandCol = g.autoExpandColumn,
49012                 gv = this;
49013         //c.beginMeasure();
49014
49015         if(!c.dom.offsetWidth){ // display:none?
49016             if(initialRender){
49017                 this.lockedWrap.show();
49018                 this.mainWrap.show();
49019             }
49020             return;
49021         }
49022
49023         var hasLock = this.cm.isLocked(0);
49024
49025         var tbh = this.headerPanel.getHeight();
49026         var bbh = this.footerPanel.getHeight();
49027
49028         if(auto){
49029             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
49030             var newHeight = ch + c.getBorderWidth("tb");
49031             if(g.maxHeight){
49032                 newHeight = Math.min(g.maxHeight, newHeight);
49033             }
49034             c.setHeight(newHeight);
49035         }
49036
49037         if(g.autoWidth){
49038             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
49039         }
49040
49041         var s = this.scroller;
49042
49043         var csize = c.getSize(true);
49044
49045         this.el.setSize(csize.width, csize.height);
49046
49047         this.headerPanel.setWidth(csize.width);
49048         this.footerPanel.setWidth(csize.width);
49049
49050         var hdHeight = this.mainHd.getHeight();
49051         var vw = csize.width;
49052         var vh = csize.height - (tbh + bbh);
49053
49054         s.setSize(vw, vh);
49055
49056         var bt = this.getBodyTable();
49057         var ltWidth = hasLock ?
49058                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
49059
49060         var scrollHeight = bt.offsetHeight;
49061         var scrollWidth = ltWidth + bt.offsetWidth;
49062         var vscroll = false, hscroll = false;
49063
49064         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
49065
49066         var lw = this.lockedWrap, mw = this.mainWrap;
49067         var lb = this.lockedBody, mb = this.mainBody;
49068
49069         setTimeout(function(){
49070             var t = s.dom.offsetTop;
49071             var w = s.dom.clientWidth,
49072                 h = s.dom.clientHeight;
49073
49074             lw.setTop(t);
49075             lw.setSize(ltWidth, h);
49076
49077             mw.setLeftTop(ltWidth, t);
49078             mw.setSize(w-ltWidth, h);
49079
49080             lb.setHeight(h-hdHeight);
49081             mb.setHeight(h-hdHeight);
49082
49083             if(is2ndPass !== true && !gv.userResized && expandCol){
49084                 // high speed resize without full column calculation
49085                 
49086                 var ci = cm.getIndexById(expandCol);
49087                 if (ci < 0) {
49088                     ci = cm.findColumnIndex(expandCol);
49089                 }
49090                 ci = Math.max(0, ci); // make sure it's got at least the first col.
49091                 var expandId = cm.getColumnId(ci);
49092                 var  tw = cm.getTotalWidth(false);
49093                 var currentWidth = cm.getColumnWidth(ci);
49094                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
49095                 if(currentWidth != cw){
49096                     cm.setColumnWidth(ci, cw, true);
49097                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49098                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49099                     gv.updateSplitters();
49100                     gv.layout(false, true);
49101                 }
49102             }
49103
49104             if(initialRender){
49105                 lw.show();
49106                 mw.show();
49107             }
49108             //c.endMeasure();
49109         }, 10);
49110     },
49111
49112     onWindowResize : function(){
49113         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
49114             return;
49115         }
49116         this.layout();
49117     },
49118
49119     appendFooter : function(parentEl){
49120         return null;
49121     },
49122
49123     sortAscText : "Sort Ascending",
49124     sortDescText : "Sort Descending",
49125     lockText : "Lock Column",
49126     unlockText : "Unlock Column",
49127     columnsText : "Columns"
49128 });
49129
49130
49131 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
49132     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
49133     this.proxy.el.addClass('x-grid3-col-dd');
49134 };
49135
49136 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
49137     handleMouseDown : function(e){
49138
49139     },
49140
49141     callHandleMouseDown : function(e){
49142         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
49143     }
49144 });
49145 /*
49146  * Based on:
49147  * Ext JS Library 1.1.1
49148  * Copyright(c) 2006-2007, Ext JS, LLC.
49149  *
49150  * Originally Released Under LGPL - original licence link has changed is not relivant.
49151  *
49152  * Fork - LGPL
49153  * <script type="text/javascript">
49154  */
49155  
49156 // private
49157 // This is a support class used internally by the Grid components
49158 Roo.grid.SplitDragZone = function(grid, hd, hd2){
49159     this.grid = grid;
49160     this.view = grid.getView();
49161     this.proxy = this.view.resizeProxy;
49162     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
49163         "gridSplitters" + this.grid.getGridEl().id, {
49164         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
49165     });
49166     this.setHandleElId(Roo.id(hd));
49167     this.setOuterHandleElId(Roo.id(hd2));
49168     this.scroll = false;
49169 };
49170 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
49171     fly: Roo.Element.fly,
49172
49173     b4StartDrag : function(x, y){
49174         this.view.headersDisabled = true;
49175         this.proxy.setHeight(this.view.mainWrap.getHeight());
49176         var w = this.cm.getColumnWidth(this.cellIndex);
49177         var minw = Math.max(w-this.grid.minColumnWidth, 0);
49178         this.resetConstraints();
49179         this.setXConstraint(minw, 1000);
49180         this.setYConstraint(0, 0);
49181         this.minX = x - minw;
49182         this.maxX = x + 1000;
49183         this.startPos = x;
49184         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
49185     },
49186
49187
49188     handleMouseDown : function(e){
49189         ev = Roo.EventObject.setEvent(e);
49190         var t = this.fly(ev.getTarget());
49191         if(t.hasClass("x-grid-split")){
49192             this.cellIndex = this.view.getCellIndex(t.dom);
49193             this.split = t.dom;
49194             this.cm = this.grid.colModel;
49195             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
49196                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
49197             }
49198         }
49199     },
49200
49201     endDrag : function(e){
49202         this.view.headersDisabled = false;
49203         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
49204         var diff = endX - this.startPos;
49205         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
49206     },
49207
49208     autoOffset : function(){
49209         this.setDelta(0,0);
49210     }
49211 });/*
49212  * Based on:
49213  * Ext JS Library 1.1.1
49214  * Copyright(c) 2006-2007, Ext JS, LLC.
49215  *
49216  * Originally Released Under LGPL - original licence link has changed is not relivant.
49217  *
49218  * Fork - LGPL
49219  * <script type="text/javascript">
49220  */
49221  
49222 // private
49223 // This is a support class used internally by the Grid components
49224 Roo.grid.GridDragZone = function(grid, config){
49225     this.view = grid.getView();
49226     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
49227     if(this.view.lockedBody){
49228         this.setHandleElId(Roo.id(this.view.mainBody.dom));
49229         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
49230     }
49231     this.scroll = false;
49232     this.grid = grid;
49233     this.ddel = document.createElement('div');
49234     this.ddel.className = 'x-grid-dd-wrap';
49235 };
49236
49237 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
49238     ddGroup : "GridDD",
49239
49240     getDragData : function(e){
49241         var t = Roo.lib.Event.getTarget(e);
49242         var rowIndex = this.view.findRowIndex(t);
49243         if(rowIndex !== false){
49244             var sm = this.grid.selModel;
49245             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
49246               //  sm.mouseDown(e, t);
49247             //}
49248             if (e.hasModifier()){
49249                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
49250             }
49251             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
49252         }
49253         return false;
49254     },
49255
49256     onInitDrag : function(e){
49257         var data = this.dragData;
49258         this.ddel.innerHTML = this.grid.getDragDropText();
49259         this.proxy.update(this.ddel);
49260         // fire start drag?
49261     },
49262
49263     afterRepair : function(){
49264         this.dragging = false;
49265     },
49266
49267     getRepairXY : function(e, data){
49268         return false;
49269     },
49270
49271     onEndDrag : function(data, e){
49272         // fire end drag?
49273     },
49274
49275     onValidDrop : function(dd, e, id){
49276         // fire drag drop?
49277         this.hideProxy();
49278     },
49279
49280     beforeInvalidDrop : function(e, id){
49281
49282     }
49283 });/*
49284  * Based on:
49285  * Ext JS Library 1.1.1
49286  * Copyright(c) 2006-2007, Ext JS, LLC.
49287  *
49288  * Originally Released Under LGPL - original licence link has changed is not relivant.
49289  *
49290  * Fork - LGPL
49291  * <script type="text/javascript">
49292  */
49293  
49294
49295 /**
49296  * @class Roo.grid.ColumnModel
49297  * @extends Roo.util.Observable
49298  * This is the default implementation of a ColumnModel used by the Grid. It defines
49299  * the columns in the grid.
49300  * <br>Usage:<br>
49301  <pre><code>
49302  var colModel = new Roo.grid.ColumnModel([
49303         {header: "Ticker", width: 60, sortable: true, locked: true},
49304         {header: "Company Name", width: 150, sortable: true},
49305         {header: "Market Cap.", width: 100, sortable: true},
49306         {header: "$ Sales", width: 100, sortable: true, renderer: money},
49307         {header: "Employees", width: 100, sortable: true, resizable: false}
49308  ]);
49309  </code></pre>
49310  * <p>
49311  
49312  * The config options listed for this class are options which may appear in each
49313  * individual column definition.
49314  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
49315  * @constructor
49316  * @param {Object} config An Array of column config objects. See this class's
49317  * config objects for details.
49318 */
49319 Roo.grid.ColumnModel = function(config){
49320         /**
49321      * The config passed into the constructor
49322      */
49323     this.config = config;
49324     this.lookup = {};
49325
49326     // if no id, create one
49327     // if the column does not have a dataIndex mapping,
49328     // map it to the order it is in the config
49329     for(var i = 0, len = config.length; i < len; i++){
49330         var c = config[i];
49331         if(typeof c.dataIndex == "undefined"){
49332             c.dataIndex = i;
49333         }
49334         if(typeof c.renderer == "string"){
49335             c.renderer = Roo.util.Format[c.renderer];
49336         }
49337         if(typeof c.id == "undefined"){
49338             c.id = Roo.id();
49339         }
49340         if(c.editor && c.editor.xtype){
49341             c.editor  = Roo.factory(c.editor, Roo.grid);
49342         }
49343         if(c.editor && c.editor.isFormField){
49344             c.editor = new Roo.grid.GridEditor(c.editor);
49345         }
49346         this.lookup[c.id] = c;
49347     }
49348
49349     /**
49350      * The width of columns which have no width specified (defaults to 100)
49351      * @type Number
49352      */
49353     this.defaultWidth = 100;
49354
49355     /**
49356      * Default sortable of columns which have no sortable specified (defaults to false)
49357      * @type Boolean
49358      */
49359     this.defaultSortable = false;
49360
49361     this.addEvents({
49362         /**
49363              * @event widthchange
49364              * Fires when the width of a column changes.
49365              * @param {ColumnModel} this
49366              * @param {Number} columnIndex The column index
49367              * @param {Number} newWidth The new width
49368              */
49369             "widthchange": true,
49370         /**
49371              * @event headerchange
49372              * Fires when the text of a header changes.
49373              * @param {ColumnModel} this
49374              * @param {Number} columnIndex The column index
49375              * @param {Number} newText The new header text
49376              */
49377             "headerchange": true,
49378         /**
49379              * @event hiddenchange
49380              * Fires when a column is hidden or "unhidden".
49381              * @param {ColumnModel} this
49382              * @param {Number} columnIndex The column index
49383              * @param {Boolean} hidden true if hidden, false otherwise
49384              */
49385             "hiddenchange": true,
49386             /**
49387          * @event columnmoved
49388          * Fires when a column is moved.
49389          * @param {ColumnModel} this
49390          * @param {Number} oldIndex
49391          * @param {Number} newIndex
49392          */
49393         "columnmoved" : true,
49394         /**
49395          * @event columlockchange
49396          * Fires when a column's locked state is changed
49397          * @param {ColumnModel} this
49398          * @param {Number} colIndex
49399          * @param {Boolean} locked true if locked
49400          */
49401         "columnlockchange" : true
49402     });
49403     Roo.grid.ColumnModel.superclass.constructor.call(this);
49404 };
49405 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
49406     /**
49407      * @cfg {String} header The header text to display in the Grid view.
49408      */
49409     /**
49410      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
49411      * {@link Roo.data.Record} definition from which to draw the column's value. If not
49412      * specified, the column's index is used as an index into the Record's data Array.
49413      */
49414     /**
49415      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
49416      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
49417      */
49418     /**
49419      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
49420      * Defaults to the value of the {@link #defaultSortable} property.
49421      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
49422      */
49423     /**
49424      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
49425      */
49426     /**
49427      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
49428      */
49429     /**
49430      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
49431      */
49432     /**
49433      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
49434      */
49435     /**
49436      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
49437      * given the cell's data value. See {@link #setRenderer}. If not specified, the
49438      * default renderer uses the raw data value.
49439      */
49440        /**
49441      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
49442      */
49443     /**
49444      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
49445      */
49446
49447     /**
49448      * Returns the id of the column at the specified index.
49449      * @param {Number} index The column index
49450      * @return {String} the id
49451      */
49452     getColumnId : function(index){
49453         return this.config[index].id;
49454     },
49455
49456     /**
49457      * Returns the column for a specified id.
49458      * @param {String} id The column id
49459      * @return {Object} the column
49460      */
49461     getColumnById : function(id){
49462         return this.lookup[id];
49463     },
49464
49465     
49466     /**
49467      * Returns the column for a specified dataIndex.
49468      * @param {String} dataIndex The column dataIndex
49469      * @return {Object|Boolean} the column or false if not found
49470      */
49471     getColumnByDataIndex: function(dataIndex){
49472         var index = this.findColumnIndex(dataIndex);
49473         return index > -1 ? this.config[index] : false;
49474     },
49475     
49476     /**
49477      * Returns the index for a specified column id.
49478      * @param {String} id The column id
49479      * @return {Number} the index, or -1 if not found
49480      */
49481     getIndexById : function(id){
49482         for(var i = 0, len = this.config.length; i < len; i++){
49483             if(this.config[i].id == id){
49484                 return i;
49485             }
49486         }
49487         return -1;
49488     },
49489     
49490     /**
49491      * Returns the index for a specified column dataIndex.
49492      * @param {String} dataIndex The column dataIndex
49493      * @return {Number} the index, or -1 if not found
49494      */
49495     
49496     findColumnIndex : function(dataIndex){
49497         for(var i = 0, len = this.config.length; i < len; i++){
49498             if(this.config[i].dataIndex == dataIndex){
49499                 return i;
49500             }
49501         }
49502         return -1;
49503     },
49504     
49505     
49506     moveColumn : function(oldIndex, newIndex){
49507         var c = this.config[oldIndex];
49508         this.config.splice(oldIndex, 1);
49509         this.config.splice(newIndex, 0, c);
49510         this.dataMap = null;
49511         this.fireEvent("columnmoved", this, oldIndex, newIndex);
49512     },
49513
49514     isLocked : function(colIndex){
49515         return this.config[colIndex].locked === true;
49516     },
49517
49518     setLocked : function(colIndex, value, suppressEvent){
49519         if(this.isLocked(colIndex) == value){
49520             return;
49521         }
49522         this.config[colIndex].locked = value;
49523         if(!suppressEvent){
49524             this.fireEvent("columnlockchange", this, colIndex, value);
49525         }
49526     },
49527
49528     getTotalLockedWidth : function(){
49529         var totalWidth = 0;
49530         for(var i = 0; i < this.config.length; i++){
49531             if(this.isLocked(i) && !this.isHidden(i)){
49532                 this.totalWidth += this.getColumnWidth(i);
49533             }
49534         }
49535         return totalWidth;
49536     },
49537
49538     getLockedCount : function(){
49539         for(var i = 0, len = this.config.length; i < len; i++){
49540             if(!this.isLocked(i)){
49541                 return i;
49542             }
49543         }
49544     },
49545
49546     /**
49547      * Returns the number of columns.
49548      * @return {Number}
49549      */
49550     getColumnCount : function(visibleOnly){
49551         if(visibleOnly === true){
49552             var c = 0;
49553             for(var i = 0, len = this.config.length; i < len; i++){
49554                 if(!this.isHidden(i)){
49555                     c++;
49556                 }
49557             }
49558             return c;
49559         }
49560         return this.config.length;
49561     },
49562
49563     /**
49564      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
49565      * @param {Function} fn
49566      * @param {Object} scope (optional)
49567      * @return {Array} result
49568      */
49569     getColumnsBy : function(fn, scope){
49570         var r = [];
49571         for(var i = 0, len = this.config.length; i < len; i++){
49572             var c = this.config[i];
49573             if(fn.call(scope||this, c, i) === true){
49574                 r[r.length] = c;
49575             }
49576         }
49577         return r;
49578     },
49579
49580     /**
49581      * Returns true if the specified column is sortable.
49582      * @param {Number} col The column index
49583      * @return {Boolean}
49584      */
49585     isSortable : function(col){
49586         if(typeof this.config[col].sortable == "undefined"){
49587             return this.defaultSortable;
49588         }
49589         return this.config[col].sortable;
49590     },
49591
49592     /**
49593      * Returns the rendering (formatting) function defined for the column.
49594      * @param {Number} col The column index.
49595      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
49596      */
49597     getRenderer : function(col){
49598         if(!this.config[col].renderer){
49599             return Roo.grid.ColumnModel.defaultRenderer;
49600         }
49601         return this.config[col].renderer;
49602     },
49603
49604     /**
49605      * Sets the rendering (formatting) function for a column.
49606      * @param {Number} col The column index
49607      * @param {Function} fn The function to use to process the cell's raw data
49608      * to return HTML markup for the grid view. The render function is called with
49609      * the following parameters:<ul>
49610      * <li>Data value.</li>
49611      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
49612      * <li>css A CSS style string to apply to the table cell.</li>
49613      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
49614      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
49615      * <li>Row index</li>
49616      * <li>Column index</li>
49617      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
49618      */
49619     setRenderer : function(col, fn){
49620         this.config[col].renderer = fn;
49621     },
49622
49623     /**
49624      * Returns the width for the specified column.
49625      * @param {Number} col The column index
49626      * @return {Number}
49627      */
49628     getColumnWidth : function(col){
49629         return this.config[col].width * 1 || this.defaultWidth;
49630     },
49631
49632     /**
49633      * Sets the width for a column.
49634      * @param {Number} col The column index
49635      * @param {Number} width The new width
49636      */
49637     setColumnWidth : function(col, width, suppressEvent){
49638         this.config[col].width = width;
49639         this.totalWidth = null;
49640         if(!suppressEvent){
49641              this.fireEvent("widthchange", this, col, width);
49642         }
49643     },
49644
49645     /**
49646      * Returns the total width of all columns.
49647      * @param {Boolean} includeHidden True to include hidden column widths
49648      * @return {Number}
49649      */
49650     getTotalWidth : function(includeHidden){
49651         if(!this.totalWidth){
49652             this.totalWidth = 0;
49653             for(var i = 0, len = this.config.length; i < len; i++){
49654                 if(includeHidden || !this.isHidden(i)){
49655                     this.totalWidth += this.getColumnWidth(i);
49656                 }
49657             }
49658         }
49659         return this.totalWidth;
49660     },
49661
49662     /**
49663      * Returns the header for the specified column.
49664      * @param {Number} col The column index
49665      * @return {String}
49666      */
49667     getColumnHeader : function(col){
49668         return this.config[col].header;
49669     },
49670
49671     /**
49672      * Sets the header for a column.
49673      * @param {Number} col The column index
49674      * @param {String} header The new header
49675      */
49676     setColumnHeader : function(col, header){
49677         this.config[col].header = header;
49678         this.fireEvent("headerchange", this, col, header);
49679     },
49680
49681     /**
49682      * Returns the tooltip for the specified column.
49683      * @param {Number} col The column index
49684      * @return {String}
49685      */
49686     getColumnTooltip : function(col){
49687             return this.config[col].tooltip;
49688     },
49689     /**
49690      * Sets the tooltip for a column.
49691      * @param {Number} col The column index
49692      * @param {String} tooltip The new tooltip
49693      */
49694     setColumnTooltip : function(col, tooltip){
49695             this.config[col].tooltip = tooltip;
49696     },
49697
49698     /**
49699      * Returns the dataIndex for the specified column.
49700      * @param {Number} col The column index
49701      * @return {Number}
49702      */
49703     getDataIndex : function(col){
49704         return this.config[col].dataIndex;
49705     },
49706
49707     /**
49708      * Sets the dataIndex for a column.
49709      * @param {Number} col The column index
49710      * @param {Number} dataIndex The new dataIndex
49711      */
49712     setDataIndex : function(col, dataIndex){
49713         this.config[col].dataIndex = dataIndex;
49714     },
49715
49716     
49717     
49718     /**
49719      * Returns true if the cell is editable.
49720      * @param {Number} colIndex The column index
49721      * @param {Number} rowIndex The row index
49722      * @return {Boolean}
49723      */
49724     isCellEditable : function(colIndex, rowIndex){
49725         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
49726     },
49727
49728     /**
49729      * Returns the editor defined for the cell/column.
49730      * return false or null to disable editing.
49731      * @param {Number} colIndex The column index
49732      * @param {Number} rowIndex The row index
49733      * @return {Object}
49734      */
49735     getCellEditor : function(colIndex, rowIndex){
49736         return this.config[colIndex].editor;
49737     },
49738
49739     /**
49740      * Sets if a column is editable.
49741      * @param {Number} col The column index
49742      * @param {Boolean} editable True if the column is editable
49743      */
49744     setEditable : function(col, editable){
49745         this.config[col].editable = editable;
49746     },
49747
49748
49749     /**
49750      * Returns true if the column is hidden.
49751      * @param {Number} colIndex The column index
49752      * @return {Boolean}
49753      */
49754     isHidden : function(colIndex){
49755         return this.config[colIndex].hidden;
49756     },
49757
49758
49759     /**
49760      * Returns true if the column width cannot be changed
49761      */
49762     isFixed : function(colIndex){
49763         return this.config[colIndex].fixed;
49764     },
49765
49766     /**
49767      * Returns true if the column can be resized
49768      * @return {Boolean}
49769      */
49770     isResizable : function(colIndex){
49771         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
49772     },
49773     /**
49774      * Sets if a column is hidden.
49775      * @param {Number} colIndex The column index
49776      * @param {Boolean} hidden True if the column is hidden
49777      */
49778     setHidden : function(colIndex, hidden){
49779         this.config[colIndex].hidden = hidden;
49780         this.totalWidth = null;
49781         this.fireEvent("hiddenchange", this, colIndex, hidden);
49782     },
49783
49784     /**
49785      * Sets the editor for a column.
49786      * @param {Number} col The column index
49787      * @param {Object} editor The editor object
49788      */
49789     setEditor : function(col, editor){
49790         this.config[col].editor = editor;
49791     }
49792 });
49793
49794 Roo.grid.ColumnModel.defaultRenderer = function(value){
49795         if(typeof value == "string" && value.length < 1){
49796             return "&#160;";
49797         }
49798         return value;
49799 };
49800
49801 // Alias for backwards compatibility
49802 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
49803 /*
49804  * Based on:
49805  * Ext JS Library 1.1.1
49806  * Copyright(c) 2006-2007, Ext JS, LLC.
49807  *
49808  * Originally Released Under LGPL - original licence link has changed is not relivant.
49809  *
49810  * Fork - LGPL
49811  * <script type="text/javascript">
49812  */
49813
49814 /**
49815  * @class Roo.grid.AbstractSelectionModel
49816  * @extends Roo.util.Observable
49817  * Abstract base class for grid SelectionModels.  It provides the interface that should be
49818  * implemented by descendant classes.  This class should not be directly instantiated.
49819  * @constructor
49820  */
49821 Roo.grid.AbstractSelectionModel = function(){
49822     this.locked = false;
49823     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
49824 };
49825
49826 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
49827     /** @ignore Called by the grid automatically. Do not call directly. */
49828     init : function(grid){
49829         this.grid = grid;
49830         this.initEvents();
49831     },
49832
49833     /**
49834      * Locks the selections.
49835      */
49836     lock : function(){
49837         this.locked = true;
49838     },
49839
49840     /**
49841      * Unlocks the selections.
49842      */
49843     unlock : function(){
49844         this.locked = false;
49845     },
49846
49847     /**
49848      * Returns true if the selections are locked.
49849      * @return {Boolean}
49850      */
49851     isLocked : function(){
49852         return this.locked;
49853     }
49854 });/*
49855  * Based on:
49856  * Ext JS Library 1.1.1
49857  * Copyright(c) 2006-2007, Ext JS, LLC.
49858  *
49859  * Originally Released Under LGPL - original licence link has changed is not relivant.
49860  *
49861  * Fork - LGPL
49862  * <script type="text/javascript">
49863  */
49864 /**
49865  * @extends Roo.grid.AbstractSelectionModel
49866  * @class Roo.grid.RowSelectionModel
49867  * The default SelectionModel used by {@link Roo.grid.Grid}.
49868  * It supports multiple selections and keyboard selection/navigation. 
49869  * @constructor
49870  * @param {Object} config
49871  */
49872 Roo.grid.RowSelectionModel = function(config){
49873     Roo.apply(this, config);
49874     this.selections = new Roo.util.MixedCollection(false, function(o){
49875         return o.id;
49876     });
49877
49878     this.last = false;
49879     this.lastActive = false;
49880
49881     this.addEvents({
49882         /**
49883              * @event selectionchange
49884              * Fires when the selection changes
49885              * @param {SelectionModel} this
49886              */
49887             "selectionchange" : true,
49888         /**
49889              * @event afterselectionchange
49890              * Fires after the selection changes (eg. by key press or clicking)
49891              * @param {SelectionModel} this
49892              */
49893             "afterselectionchange" : true,
49894         /**
49895              * @event beforerowselect
49896              * Fires when a row is selected being selected, return false to cancel.
49897              * @param {SelectionModel} this
49898              * @param {Number} rowIndex The selected index
49899              * @param {Boolean} keepExisting False if other selections will be cleared
49900              */
49901             "beforerowselect" : true,
49902         /**
49903              * @event rowselect
49904              * Fires when a row is selected.
49905              * @param {SelectionModel} this
49906              * @param {Number} rowIndex The selected index
49907              * @param {Roo.data.Record} r The record
49908              */
49909             "rowselect" : true,
49910         /**
49911              * @event rowdeselect
49912              * Fires when a row is deselected.
49913              * @param {SelectionModel} this
49914              * @param {Number} rowIndex The selected index
49915              */
49916         "rowdeselect" : true
49917     });
49918     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
49919     this.locked = false;
49920 };
49921
49922 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
49923     /**
49924      * @cfg {Boolean} singleSelect
49925      * True to allow selection of only one row at a time (defaults to false)
49926      */
49927     singleSelect : false,
49928
49929     // private
49930     initEvents : function(){
49931
49932         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
49933             this.grid.on("mousedown", this.handleMouseDown, this);
49934         }else{ // allow click to work like normal
49935             this.grid.on("rowclick", this.handleDragableRowClick, this);
49936         }
49937
49938         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
49939             "up" : function(e){
49940                 if(!e.shiftKey){
49941                     this.selectPrevious(e.shiftKey);
49942                 }else if(this.last !== false && this.lastActive !== false){
49943                     var last = this.last;
49944                     this.selectRange(this.last,  this.lastActive-1);
49945                     this.grid.getView().focusRow(this.lastActive);
49946                     if(last !== false){
49947                         this.last = last;
49948                     }
49949                 }else{
49950                     this.selectFirstRow();
49951                 }
49952                 this.fireEvent("afterselectionchange", this);
49953             },
49954             "down" : function(e){
49955                 if(!e.shiftKey){
49956                     this.selectNext(e.shiftKey);
49957                 }else if(this.last !== false && this.lastActive !== false){
49958                     var last = this.last;
49959                     this.selectRange(this.last,  this.lastActive+1);
49960                     this.grid.getView().focusRow(this.lastActive);
49961                     if(last !== false){
49962                         this.last = last;
49963                     }
49964                 }else{
49965                     this.selectFirstRow();
49966                 }
49967                 this.fireEvent("afterselectionchange", this);
49968             },
49969             scope: this
49970         });
49971
49972         var view = this.grid.view;
49973         view.on("refresh", this.onRefresh, this);
49974         view.on("rowupdated", this.onRowUpdated, this);
49975         view.on("rowremoved", this.onRemove, this);
49976     },
49977
49978     // private
49979     onRefresh : function(){
49980         var ds = this.grid.dataSource, i, v = this.grid.view;
49981         var s = this.selections;
49982         s.each(function(r){
49983             if((i = ds.indexOfId(r.id)) != -1){
49984                 v.onRowSelect(i);
49985             }else{
49986                 s.remove(r);
49987             }
49988         });
49989     },
49990
49991     // private
49992     onRemove : function(v, index, r){
49993         this.selections.remove(r);
49994     },
49995
49996     // private
49997     onRowUpdated : function(v, index, r){
49998         if(this.isSelected(r)){
49999             v.onRowSelect(index);
50000         }
50001     },
50002
50003     /**
50004      * Select records.
50005      * @param {Array} records The records to select
50006      * @param {Boolean} keepExisting (optional) True to keep existing selections
50007      */
50008     selectRecords : function(records, keepExisting){
50009         if(!keepExisting){
50010             this.clearSelections();
50011         }
50012         var ds = this.grid.dataSource;
50013         for(var i = 0, len = records.length; i < len; i++){
50014             this.selectRow(ds.indexOf(records[i]), true);
50015         }
50016     },
50017
50018     /**
50019      * Gets the number of selected rows.
50020      * @return {Number}
50021      */
50022     getCount : function(){
50023         return this.selections.length;
50024     },
50025
50026     /**
50027      * Selects the first row in the grid.
50028      */
50029     selectFirstRow : function(){
50030         this.selectRow(0);
50031     },
50032
50033     /**
50034      * Select the last row.
50035      * @param {Boolean} keepExisting (optional) True to keep existing selections
50036      */
50037     selectLastRow : function(keepExisting){
50038         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
50039     },
50040
50041     /**
50042      * Selects the row immediately following the last selected row.
50043      * @param {Boolean} keepExisting (optional) True to keep existing selections
50044      */
50045     selectNext : function(keepExisting){
50046         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
50047             this.selectRow(this.last+1, keepExisting);
50048             this.grid.getView().focusRow(this.last);
50049         }
50050     },
50051
50052     /**
50053      * Selects the row that precedes the last selected row.
50054      * @param {Boolean} keepExisting (optional) True to keep existing selections
50055      */
50056     selectPrevious : function(keepExisting){
50057         if(this.last){
50058             this.selectRow(this.last-1, keepExisting);
50059             this.grid.getView().focusRow(this.last);
50060         }
50061     },
50062
50063     /**
50064      * Returns the selected records
50065      * @return {Array} Array of selected records
50066      */
50067     getSelections : function(){
50068         return [].concat(this.selections.items);
50069     },
50070
50071     /**
50072      * Returns the first selected record.
50073      * @return {Record}
50074      */
50075     getSelected : function(){
50076         return this.selections.itemAt(0);
50077     },
50078
50079
50080     /**
50081      * Clears all selections.
50082      */
50083     clearSelections : function(fast){
50084         if(this.locked) return;
50085         if(fast !== true){
50086             var ds = this.grid.dataSource;
50087             var s = this.selections;
50088             s.each(function(r){
50089                 this.deselectRow(ds.indexOfId(r.id));
50090             }, this);
50091             s.clear();
50092         }else{
50093             this.selections.clear();
50094         }
50095         this.last = false;
50096     },
50097
50098
50099     /**
50100      * Selects all rows.
50101      */
50102     selectAll : function(){
50103         if(this.locked) return;
50104         this.selections.clear();
50105         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
50106             this.selectRow(i, true);
50107         }
50108     },
50109
50110     /**
50111      * Returns True if there is a selection.
50112      * @return {Boolean}
50113      */
50114     hasSelection : function(){
50115         return this.selections.length > 0;
50116     },
50117
50118     /**
50119      * Returns True if the specified row is selected.
50120      * @param {Number/Record} record The record or index of the record to check
50121      * @return {Boolean}
50122      */
50123     isSelected : function(index){
50124         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
50125         return (r && this.selections.key(r.id) ? true : false);
50126     },
50127
50128     /**
50129      * Returns True if the specified record id is selected.
50130      * @param {String} id The id of record to check
50131      * @return {Boolean}
50132      */
50133     isIdSelected : function(id){
50134         return (this.selections.key(id) ? true : false);
50135     },
50136
50137     // private
50138     handleMouseDown : function(e, t){
50139         var view = this.grid.getView(), rowIndex;
50140         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
50141             return;
50142         };
50143         if(e.shiftKey && this.last !== false){
50144             var last = this.last;
50145             this.selectRange(last, rowIndex, e.ctrlKey);
50146             this.last = last; // reset the last
50147             view.focusRow(rowIndex);
50148         }else{
50149             var isSelected = this.isSelected(rowIndex);
50150             if(e.button !== 0 && isSelected){
50151                 view.focusRow(rowIndex);
50152             }else if(e.ctrlKey && isSelected){
50153                 this.deselectRow(rowIndex);
50154             }else if(!isSelected){
50155                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
50156                 view.focusRow(rowIndex);
50157             }
50158         }
50159         this.fireEvent("afterselectionchange", this);
50160     },
50161     // private
50162     handleDragableRowClick :  function(grid, rowIndex, e) 
50163     {
50164         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
50165             this.selectRow(rowIndex, false);
50166             grid.view.focusRow(rowIndex);
50167              this.fireEvent("afterselectionchange", this);
50168         }
50169     },
50170     
50171     /**
50172      * Selects multiple rows.
50173      * @param {Array} rows Array of the indexes of the row to select
50174      * @param {Boolean} keepExisting (optional) True to keep existing selections
50175      */
50176     selectRows : function(rows, keepExisting){
50177         if(!keepExisting){
50178             this.clearSelections();
50179         }
50180         for(var i = 0, len = rows.length; i < len; i++){
50181             this.selectRow(rows[i], true);
50182         }
50183     },
50184
50185     /**
50186      * Selects a range of rows. All rows in between startRow and endRow are also selected.
50187      * @param {Number} startRow The index of the first row in the range
50188      * @param {Number} endRow The index of the last row in the range
50189      * @param {Boolean} keepExisting (optional) True to retain existing selections
50190      */
50191     selectRange : function(startRow, endRow, keepExisting){
50192         if(this.locked) return;
50193         if(!keepExisting){
50194             this.clearSelections();
50195         }
50196         if(startRow <= endRow){
50197             for(var i = startRow; i <= endRow; i++){
50198                 this.selectRow(i, true);
50199             }
50200         }else{
50201             for(var i = startRow; i >= endRow; i--){
50202                 this.selectRow(i, true);
50203             }
50204         }
50205     },
50206
50207     /**
50208      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
50209      * @param {Number} startRow The index of the first row in the range
50210      * @param {Number} endRow The index of the last row in the range
50211      */
50212     deselectRange : function(startRow, endRow, preventViewNotify){
50213         if(this.locked) return;
50214         for(var i = startRow; i <= endRow; i++){
50215             this.deselectRow(i, preventViewNotify);
50216         }
50217     },
50218
50219     /**
50220      * Selects a row.
50221      * @param {Number} row The index of the row to select
50222      * @param {Boolean} keepExisting (optional) True to keep existing selections
50223      */
50224     selectRow : function(index, keepExisting, preventViewNotify){
50225         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
50226         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
50227             if(!keepExisting || this.singleSelect){
50228                 this.clearSelections();
50229             }
50230             var r = this.grid.dataSource.getAt(index);
50231             this.selections.add(r);
50232             this.last = this.lastActive = index;
50233             if(!preventViewNotify){
50234                 this.grid.getView().onRowSelect(index);
50235             }
50236             this.fireEvent("rowselect", this, index, r);
50237             this.fireEvent("selectionchange", this);
50238         }
50239     },
50240
50241     /**
50242      * Deselects a row.
50243      * @param {Number} row The index of the row to deselect
50244      */
50245     deselectRow : function(index, preventViewNotify){
50246         if(this.locked) return;
50247         if(this.last == index){
50248             this.last = false;
50249         }
50250         if(this.lastActive == index){
50251             this.lastActive = false;
50252         }
50253         var r = this.grid.dataSource.getAt(index);
50254         this.selections.remove(r);
50255         if(!preventViewNotify){
50256             this.grid.getView().onRowDeselect(index);
50257         }
50258         this.fireEvent("rowdeselect", this, index);
50259         this.fireEvent("selectionchange", this);
50260     },
50261
50262     // private
50263     restoreLast : function(){
50264         if(this._last){
50265             this.last = this._last;
50266         }
50267     },
50268
50269     // private
50270     acceptsNav : function(row, col, cm){
50271         return !cm.isHidden(col) && cm.isCellEditable(col, row);
50272     },
50273
50274     // private
50275     onEditorKey : function(field, e){
50276         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
50277         if(k == e.TAB){
50278             e.stopEvent();
50279             ed.completeEdit();
50280             if(e.shiftKey){
50281                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
50282             }else{
50283                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50284             }
50285         }else if(k == e.ENTER && !e.ctrlKey){
50286             e.stopEvent();
50287             ed.completeEdit();
50288             if(e.shiftKey){
50289                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
50290             }else{
50291                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
50292             }
50293         }else if(k == e.ESC){
50294             ed.cancelEdit();
50295         }
50296         if(newCell){
50297             g.startEditing(newCell[0], newCell[1]);
50298         }
50299     }
50300 });/*
50301  * Based on:
50302  * Ext JS Library 1.1.1
50303  * Copyright(c) 2006-2007, Ext JS, LLC.
50304  *
50305  * Originally Released Under LGPL - original licence link has changed is not relivant.
50306  *
50307  * Fork - LGPL
50308  * <script type="text/javascript">
50309  */
50310 /**
50311  * @class Roo.grid.CellSelectionModel
50312  * @extends Roo.grid.AbstractSelectionModel
50313  * This class provides the basic implementation for cell selection in a grid.
50314  * @constructor
50315  * @param {Object} config The object containing the configuration of this model.
50316  */
50317 Roo.grid.CellSelectionModel = function(config){
50318     Roo.apply(this, config);
50319
50320     this.selection = null;
50321
50322     this.addEvents({
50323         /**
50324              * @event beforerowselect
50325              * Fires before a cell is selected.
50326              * @param {SelectionModel} this
50327              * @param {Number} rowIndex The selected row index
50328              * @param {Number} colIndex The selected cell index
50329              */
50330             "beforecellselect" : true,
50331         /**
50332              * @event cellselect
50333              * Fires when a cell is selected.
50334              * @param {SelectionModel} this
50335              * @param {Number} rowIndex The selected row index
50336              * @param {Number} colIndex The selected cell index
50337              */
50338             "cellselect" : true,
50339         /**
50340              * @event selectionchange
50341              * Fires when the active selection changes.
50342              * @param {SelectionModel} this
50343              * @param {Object} selection null for no selection or an object (o) with two properties
50344                 <ul>
50345                 <li>o.record: the record object for the row the selection is in</li>
50346                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
50347                 </ul>
50348              */
50349             "selectionchange" : true
50350     });
50351     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
50352 };
50353
50354 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
50355
50356     /** @ignore */
50357     initEvents : function(){
50358         this.grid.on("mousedown", this.handleMouseDown, this);
50359         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
50360         var view = this.grid.view;
50361         view.on("refresh", this.onViewChange, this);
50362         view.on("rowupdated", this.onRowUpdated, this);
50363         view.on("beforerowremoved", this.clearSelections, this);
50364         view.on("beforerowsinserted", this.clearSelections, this);
50365         if(this.grid.isEditor){
50366             this.grid.on("beforeedit", this.beforeEdit,  this);
50367         }
50368     },
50369
50370         //private
50371     beforeEdit : function(e){
50372         this.select(e.row, e.column, false, true, e.record);
50373     },
50374
50375         //private
50376     onRowUpdated : function(v, index, r){
50377         if(this.selection && this.selection.record == r){
50378             v.onCellSelect(index, this.selection.cell[1]);
50379         }
50380     },
50381
50382         //private
50383     onViewChange : function(){
50384         this.clearSelections(true);
50385     },
50386
50387         /**
50388          * Returns the currently selected cell,.
50389          * @return {Array} The selected cell (row, column) or null if none selected.
50390          */
50391     getSelectedCell : function(){
50392         return this.selection ? this.selection.cell : null;
50393     },
50394
50395     /**
50396      * Clears all selections.
50397      * @param {Boolean} true to prevent the gridview from being notified about the change.
50398      */
50399     clearSelections : function(preventNotify){
50400         var s = this.selection;
50401         if(s){
50402             if(preventNotify !== true){
50403                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
50404             }
50405             this.selection = null;
50406             this.fireEvent("selectionchange", this, null);
50407         }
50408     },
50409
50410     /**
50411      * Returns true if there is a selection.
50412      * @return {Boolean}
50413      */
50414     hasSelection : function(){
50415         return this.selection ? true : false;
50416     },
50417
50418     /** @ignore */
50419     handleMouseDown : function(e, t){
50420         var v = this.grid.getView();
50421         if(this.isLocked()){
50422             return;
50423         };
50424         var row = v.findRowIndex(t);
50425         var cell = v.findCellIndex(t);
50426         if(row !== false && cell !== false){
50427             this.select(row, cell);
50428         }
50429     },
50430
50431     /**
50432      * Selects a cell.
50433      * @param {Number} rowIndex
50434      * @param {Number} collIndex
50435      */
50436     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
50437         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
50438             this.clearSelections();
50439             r = r || this.grid.dataSource.getAt(rowIndex);
50440             this.selection = {
50441                 record : r,
50442                 cell : [rowIndex, colIndex]
50443             };
50444             if(!preventViewNotify){
50445                 var v = this.grid.getView();
50446                 v.onCellSelect(rowIndex, colIndex);
50447                 if(preventFocus !== true){
50448                     v.focusCell(rowIndex, colIndex);
50449                 }
50450             }
50451             this.fireEvent("cellselect", this, rowIndex, colIndex);
50452             this.fireEvent("selectionchange", this, this.selection);
50453         }
50454     },
50455
50456         //private
50457     isSelectable : function(rowIndex, colIndex, cm){
50458         return !cm.isHidden(colIndex);
50459     },
50460
50461     /** @ignore */
50462     handleKeyDown : function(e){
50463         Roo.log('Cell Sel Model handleKeyDown');
50464         if(!e.isNavKeyPress()){
50465             return;
50466         }
50467         var g = this.grid, s = this.selection;
50468         if(!s){
50469             e.stopEvent();
50470             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
50471             if(cell){
50472                 this.select(cell[0], cell[1]);
50473             }
50474             return;
50475         }
50476         var sm = this;
50477         var walk = function(row, col, step){
50478             return g.walkCells(row, col, step, sm.isSelectable,  sm);
50479         };
50480         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
50481         var newCell;
50482
50483         switch(k){
50484             case e.TAB:
50485                 // handled by onEditorKey
50486                 if (g.isEditor && g.editing) {
50487                     return;
50488                 }
50489                 if(e.shiftKey){
50490                      newCell = walk(r, c-1, -1);
50491                 }else{
50492                      newCell = walk(r, c+1, 1);
50493                 }
50494              break;
50495              case e.DOWN:
50496                  newCell = walk(r+1, c, 1);
50497              break;
50498              case e.UP:
50499                  newCell = walk(r-1, c, -1);
50500              break;
50501              case e.RIGHT:
50502                  newCell = walk(r, c+1, 1);
50503              break;
50504              case e.LEFT:
50505                  newCell = walk(r, c-1, -1);
50506              break;
50507              case e.ENTER:
50508                  if(g.isEditor && !g.editing){
50509                     g.startEditing(r, c);
50510                     e.stopEvent();
50511                     return;
50512                 }
50513              break;
50514         };
50515         if(newCell){
50516             this.select(newCell[0], newCell[1]);
50517             e.stopEvent();
50518         }
50519     },
50520
50521     acceptsNav : function(row, col, cm){
50522         return !cm.isHidden(col) && cm.isCellEditable(col, row);
50523     },
50524
50525     onEditorKey : function(field, e){
50526         
50527         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
50528         ///Roo.log('onEditorKey' + k);
50529         
50530         if(k == e.TAB){
50531             if(e.shiftKey){
50532                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
50533             }else{
50534                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50535             }
50536             e.stopEvent();
50537         }else if(k == e.ENTER && !e.ctrlKey){
50538             ed.completeEdit();
50539             e.stopEvent();
50540             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50541         }else if(k == e.ESC){
50542             ed.cancelEdit();
50543         }
50544         
50545         
50546         if(newCell){
50547             //Roo.log('next cell after edit');
50548             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
50549         }
50550     }
50551 });/*
50552  * Based on:
50553  * Ext JS Library 1.1.1
50554  * Copyright(c) 2006-2007, Ext JS, LLC.
50555  *
50556  * Originally Released Under LGPL - original licence link has changed is not relivant.
50557  *
50558  * Fork - LGPL
50559  * <script type="text/javascript">
50560  */
50561  
50562 /**
50563  * @class Roo.grid.EditorGrid
50564  * @extends Roo.grid.Grid
50565  * Class for creating and editable grid.
50566  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
50567  * The container MUST have some type of size defined for the grid to fill. The container will be 
50568  * automatically set to position relative if it isn't already.
50569  * @param {Object} dataSource The data model to bind to
50570  * @param {Object} colModel The column model with info about this grid's columns
50571  */
50572 Roo.grid.EditorGrid = function(container, config){
50573     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
50574     this.getGridEl().addClass("xedit-grid");
50575
50576     if(!this.selModel){
50577         this.selModel = new Roo.grid.CellSelectionModel();
50578     }
50579
50580     this.activeEditor = null;
50581
50582         this.addEvents({
50583             /**
50584              * @event beforeedit
50585              * Fires before cell editing is triggered. The edit event object has the following properties <br />
50586              * <ul style="padding:5px;padding-left:16px;">
50587              * <li>grid - This grid</li>
50588              * <li>record - The record being edited</li>
50589              * <li>field - The field name being edited</li>
50590              * <li>value - The value for the field being edited.</li>
50591              * <li>row - The grid row index</li>
50592              * <li>column - The grid column index</li>
50593              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50594              * </ul>
50595              * @param {Object} e An edit event (see above for description)
50596              */
50597             "beforeedit" : true,
50598             /**
50599              * @event afteredit
50600              * Fires after a cell is edited. <br />
50601              * <ul style="padding:5px;padding-left:16px;">
50602              * <li>grid - This grid</li>
50603              * <li>record - The record being edited</li>
50604              * <li>field - The field name being edited</li>
50605              * <li>value - The value being set</li>
50606              * <li>originalValue - The original value for the field, before the edit.</li>
50607              * <li>row - The grid row index</li>
50608              * <li>column - The grid column index</li>
50609              * </ul>
50610              * @param {Object} e An edit event (see above for description)
50611              */
50612             "afteredit" : true,
50613             /**
50614              * @event validateedit
50615              * Fires after a cell is edited, but before the value is set in the record. 
50616          * You can use this to modify the value being set in the field, Return false
50617              * to cancel the change. The edit event object has the following properties <br />
50618              * <ul style="padding:5px;padding-left:16px;">
50619          * <li>editor - This editor</li>
50620              * <li>grid - This grid</li>
50621              * <li>record - The record being edited</li>
50622              * <li>field - The field name being edited</li>
50623              * <li>value - The value being set</li>
50624              * <li>originalValue - The original value for the field, before the edit.</li>
50625              * <li>row - The grid row index</li>
50626              * <li>column - The grid column index</li>
50627              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50628              * </ul>
50629              * @param {Object} e An edit event (see above for description)
50630              */
50631             "validateedit" : true
50632         });
50633     this.on("bodyscroll", this.stopEditing,  this);
50634     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
50635 };
50636
50637 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
50638     /**
50639      * @cfg {Number} clicksToEdit
50640      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
50641      */
50642     clicksToEdit: 2,
50643
50644     // private
50645     isEditor : true,
50646     // private
50647     trackMouseOver: false, // causes very odd FF errors
50648
50649     onCellDblClick : function(g, row, col){
50650         this.startEditing(row, col);
50651     },
50652
50653     onEditComplete : function(ed, value, startValue){
50654         this.editing = false;
50655         this.activeEditor = null;
50656         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
50657         var r = ed.record;
50658         var field = this.colModel.getDataIndex(ed.col);
50659         var e = {
50660             grid: this,
50661             record: r,
50662             field: field,
50663             originalValue: startValue,
50664             value: value,
50665             row: ed.row,
50666             column: ed.col,
50667             cancel:false,
50668             editor: ed
50669         };
50670         if(String(value) !== String(startValue)){
50671             
50672             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
50673                 r.set(field, e.value);
50674                 // if we are dealing with a combo box..
50675                 // then we also set the 'name' colum to be the displayField
50676                 if (ed.field.displayField && ed.field.name) {
50677                     r.set(ed.field.name, ed.field.el.dom.value);
50678                 }
50679                 
50680                 delete e.cancel; //?? why!!!
50681                 this.fireEvent("afteredit", e);
50682             }
50683         } else {
50684             this.fireEvent("afteredit", e); // always fire it!
50685         }
50686         this.view.focusCell(ed.row, ed.col);
50687     },
50688
50689     /**
50690      * Starts editing the specified for the specified row/column
50691      * @param {Number} rowIndex
50692      * @param {Number} colIndex
50693      */
50694     startEditing : function(row, col){
50695         this.stopEditing();
50696         if(this.colModel.isCellEditable(col, row)){
50697             this.view.ensureVisible(row, col, true);
50698             var r = this.dataSource.getAt(row);
50699             var field = this.colModel.getDataIndex(col);
50700             var e = {
50701                 grid: this,
50702                 record: r,
50703                 field: field,
50704                 value: r.data[field],
50705                 row: row,
50706                 column: col,
50707                 cancel:false
50708             };
50709             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
50710                 this.editing = true;
50711                 var ed = this.colModel.getCellEditor(col, row);
50712                 
50713                 if (!ed) {
50714                     return;
50715                 }
50716                 if(!ed.rendered){
50717                     ed.render(ed.parentEl || document.body);
50718                 }
50719                 ed.field.reset();
50720                 (function(){ // complex but required for focus issues in safari, ie and opera
50721                     ed.row = row;
50722                     ed.col = col;
50723                     ed.record = r;
50724                     ed.on("complete", this.onEditComplete, this, {single: true});
50725                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
50726                     this.activeEditor = ed;
50727                     var v = r.data[field];
50728                     ed.startEdit(this.view.getCell(row, col), v);
50729                     // combo's with 'displayField and name set
50730                     if (ed.field.displayField && ed.field.name) {
50731                         ed.field.el.dom.value = r.data[ed.field.name];
50732                     }
50733                     
50734                     
50735                 }).defer(50, this);
50736             }
50737         }
50738     },
50739         
50740     /**
50741      * Stops any active editing
50742      */
50743     stopEditing : function(){
50744         if(this.activeEditor){
50745             this.activeEditor.completeEdit();
50746         }
50747         this.activeEditor = null;
50748     }
50749 });/*
50750  * Based on:
50751  * Ext JS Library 1.1.1
50752  * Copyright(c) 2006-2007, Ext JS, LLC.
50753  *
50754  * Originally Released Under LGPL - original licence link has changed is not relivant.
50755  *
50756  * Fork - LGPL
50757  * <script type="text/javascript">
50758  */
50759
50760 // private - not really -- you end up using it !
50761 // This is a support class used internally by the Grid components
50762
50763 /**
50764  * @class Roo.grid.GridEditor
50765  * @extends Roo.Editor
50766  * Class for creating and editable grid elements.
50767  * @param {Object} config any settings (must include field)
50768  */
50769 Roo.grid.GridEditor = function(field, config){
50770     if (!config && field.field) {
50771         config = field;
50772         field = Roo.factory(config.field, Roo.form);
50773     }
50774     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
50775     field.monitorTab = false;
50776 };
50777
50778 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
50779     
50780     /**
50781      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
50782      */
50783     
50784     alignment: "tl-tl",
50785     autoSize: "width",
50786     hideEl : false,
50787     cls: "x-small-editor x-grid-editor",
50788     shim:false,
50789     shadow:"frame"
50790 });/*
50791  * Based on:
50792  * Ext JS Library 1.1.1
50793  * Copyright(c) 2006-2007, Ext JS, LLC.
50794  *
50795  * Originally Released Under LGPL - original licence link has changed is not relivant.
50796  *
50797  * Fork - LGPL
50798  * <script type="text/javascript">
50799  */
50800   
50801
50802   
50803 Roo.grid.PropertyRecord = Roo.data.Record.create([
50804     {name:'name',type:'string'},  'value'
50805 ]);
50806
50807
50808 Roo.grid.PropertyStore = function(grid, source){
50809     this.grid = grid;
50810     this.store = new Roo.data.Store({
50811         recordType : Roo.grid.PropertyRecord
50812     });
50813     this.store.on('update', this.onUpdate,  this);
50814     if(source){
50815         this.setSource(source);
50816     }
50817     Roo.grid.PropertyStore.superclass.constructor.call(this);
50818 };
50819
50820
50821
50822 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
50823     setSource : function(o){
50824         this.source = o;
50825         this.store.removeAll();
50826         var data = [];
50827         for(var k in o){
50828             if(this.isEditableValue(o[k])){
50829                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
50830             }
50831         }
50832         this.store.loadRecords({records: data}, {}, true);
50833     },
50834
50835     onUpdate : function(ds, record, type){
50836         if(type == Roo.data.Record.EDIT){
50837             var v = record.data['value'];
50838             var oldValue = record.modified['value'];
50839             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
50840                 this.source[record.id] = v;
50841                 record.commit();
50842                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
50843             }else{
50844                 record.reject();
50845             }
50846         }
50847     },
50848
50849     getProperty : function(row){
50850        return this.store.getAt(row);
50851     },
50852
50853     isEditableValue: function(val){
50854         if(val && val instanceof Date){
50855             return true;
50856         }else if(typeof val == 'object' || typeof val == 'function'){
50857             return false;
50858         }
50859         return true;
50860     },
50861
50862     setValue : function(prop, value){
50863         this.source[prop] = value;
50864         this.store.getById(prop).set('value', value);
50865     },
50866
50867     getSource : function(){
50868         return this.source;
50869     }
50870 });
50871
50872 Roo.grid.PropertyColumnModel = function(grid, store){
50873     this.grid = grid;
50874     var g = Roo.grid;
50875     g.PropertyColumnModel.superclass.constructor.call(this, [
50876         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
50877         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
50878     ]);
50879     this.store = store;
50880     this.bselect = Roo.DomHelper.append(document.body, {
50881         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
50882             {tag: 'option', value: 'true', html: 'true'},
50883             {tag: 'option', value: 'false', html: 'false'}
50884         ]
50885     });
50886     Roo.id(this.bselect);
50887     var f = Roo.form;
50888     this.editors = {
50889         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
50890         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
50891         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
50892         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
50893         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
50894     };
50895     this.renderCellDelegate = this.renderCell.createDelegate(this);
50896     this.renderPropDelegate = this.renderProp.createDelegate(this);
50897 };
50898
50899 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
50900     
50901     
50902     nameText : 'Name',
50903     valueText : 'Value',
50904     
50905     dateFormat : 'm/j/Y',
50906     
50907     
50908     renderDate : function(dateVal){
50909         return dateVal.dateFormat(this.dateFormat);
50910     },
50911
50912     renderBool : function(bVal){
50913         return bVal ? 'true' : 'false';
50914     },
50915
50916     isCellEditable : function(colIndex, rowIndex){
50917         return colIndex == 1;
50918     },
50919
50920     getRenderer : function(col){
50921         return col == 1 ?
50922             this.renderCellDelegate : this.renderPropDelegate;
50923     },
50924
50925     renderProp : function(v){
50926         return this.getPropertyName(v);
50927     },
50928
50929     renderCell : function(val){
50930         var rv = val;
50931         if(val instanceof Date){
50932             rv = this.renderDate(val);
50933         }else if(typeof val == 'boolean'){
50934             rv = this.renderBool(val);
50935         }
50936         return Roo.util.Format.htmlEncode(rv);
50937     },
50938
50939     getPropertyName : function(name){
50940         var pn = this.grid.propertyNames;
50941         return pn && pn[name] ? pn[name] : name;
50942     },
50943
50944     getCellEditor : function(colIndex, rowIndex){
50945         var p = this.store.getProperty(rowIndex);
50946         var n = p.data['name'], val = p.data['value'];
50947         
50948         if(typeof(this.grid.customEditors[n]) == 'string'){
50949             return this.editors[this.grid.customEditors[n]];
50950         }
50951         if(typeof(this.grid.customEditors[n]) != 'undefined'){
50952             return this.grid.customEditors[n];
50953         }
50954         if(val instanceof Date){
50955             return this.editors['date'];
50956         }else if(typeof val == 'number'){
50957             return this.editors['number'];
50958         }else if(typeof val == 'boolean'){
50959             return this.editors['boolean'];
50960         }else{
50961             return this.editors['string'];
50962         }
50963     }
50964 });
50965
50966 /**
50967  * @class Roo.grid.PropertyGrid
50968  * @extends Roo.grid.EditorGrid
50969  * This class represents the  interface of a component based property grid control.
50970  * <br><br>Usage:<pre><code>
50971  var grid = new Roo.grid.PropertyGrid("my-container-id", {
50972       
50973  });
50974  // set any options
50975  grid.render();
50976  * </code></pre>
50977   
50978  * @constructor
50979  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
50980  * The container MUST have some type of size defined for the grid to fill. The container will be
50981  * automatically set to position relative if it isn't already.
50982  * @param {Object} config A config object that sets properties on this grid.
50983  */
50984 Roo.grid.PropertyGrid = function(container, config){
50985     config = config || {};
50986     var store = new Roo.grid.PropertyStore(this);
50987     this.store = store;
50988     var cm = new Roo.grid.PropertyColumnModel(this, store);
50989     store.store.sort('name', 'ASC');
50990     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
50991         ds: store.store,
50992         cm: cm,
50993         enableColLock:false,
50994         enableColumnMove:false,
50995         stripeRows:false,
50996         trackMouseOver: false,
50997         clicksToEdit:1
50998     }, config));
50999     this.getGridEl().addClass('x-props-grid');
51000     this.lastEditRow = null;
51001     this.on('columnresize', this.onColumnResize, this);
51002     this.addEvents({
51003          /**
51004              * @event beforepropertychange
51005              * Fires before a property changes (return false to stop?)
51006              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51007              * @param {String} id Record Id
51008              * @param {String} newval New Value
51009          * @param {String} oldval Old Value
51010              */
51011         "beforepropertychange": true,
51012         /**
51013              * @event propertychange
51014              * Fires after a property changes
51015              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51016              * @param {String} id Record Id
51017              * @param {String} newval New Value
51018          * @param {String} oldval Old Value
51019              */
51020         "propertychange": true
51021     });
51022     this.customEditors = this.customEditors || {};
51023 };
51024 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
51025     
51026      /**
51027      * @cfg {Object} customEditors map of colnames=> custom editors.
51028      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
51029      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
51030      * false disables editing of the field.
51031          */
51032     
51033       /**
51034      * @cfg {Object} propertyNames map of property Names to their displayed value
51035          */
51036     
51037     render : function(){
51038         Roo.grid.PropertyGrid.superclass.render.call(this);
51039         this.autoSize.defer(100, this);
51040     },
51041
51042     autoSize : function(){
51043         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
51044         if(this.view){
51045             this.view.fitColumns();
51046         }
51047     },
51048
51049     onColumnResize : function(){
51050         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
51051         this.autoSize();
51052     },
51053     /**
51054      * Sets the data for the Grid
51055      * accepts a Key => Value object of all the elements avaiable.
51056      * @param {Object} data  to appear in grid.
51057      */
51058     setSource : function(source){
51059         this.store.setSource(source);
51060         //this.autoSize();
51061     },
51062     /**
51063      * Gets all the data from the grid.
51064      * @return {Object} data  data stored in grid
51065      */
51066     getSource : function(){
51067         return this.store.getSource();
51068     }
51069 });/*
51070  * Based on:
51071  * Ext JS Library 1.1.1
51072  * Copyright(c) 2006-2007, Ext JS, LLC.
51073  *
51074  * Originally Released Under LGPL - original licence link has changed is not relivant.
51075  *
51076  * Fork - LGPL
51077  * <script type="text/javascript">
51078  */
51079  
51080 /**
51081  * @class Roo.LoadMask
51082  * A simple utility class for generically masking elements while loading data.  If the element being masked has
51083  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
51084  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
51085  * element's UpdateManager load indicator and will be destroyed after the initial load.
51086  * @constructor
51087  * Create a new LoadMask
51088  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
51089  * @param {Object} config The config object
51090  */
51091 Roo.LoadMask = function(el, config){
51092     this.el = Roo.get(el);
51093     Roo.apply(this, config);
51094     if(this.store){
51095         this.store.on('beforeload', this.onBeforeLoad, this);
51096         this.store.on('load', this.onLoad, this);
51097         this.store.on('loadexception', this.onLoad, this);
51098         this.removeMask = false;
51099     }else{
51100         var um = this.el.getUpdateManager();
51101         um.showLoadIndicator = false; // disable the default indicator
51102         um.on('beforeupdate', this.onBeforeLoad, this);
51103         um.on('update', this.onLoad, this);
51104         um.on('failure', this.onLoad, this);
51105         this.removeMask = true;
51106     }
51107 };
51108
51109 Roo.LoadMask.prototype = {
51110     /**
51111      * @cfg {Boolean} removeMask
51112      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
51113      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
51114      */
51115     /**
51116      * @cfg {String} msg
51117      * The text to display in a centered loading message box (defaults to 'Loading...')
51118      */
51119     msg : 'Loading...',
51120     /**
51121      * @cfg {String} msgCls
51122      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
51123      */
51124     msgCls : 'x-mask-loading',
51125
51126     /**
51127      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
51128      * @type Boolean
51129      */
51130     disabled: false,
51131
51132     /**
51133      * Disables the mask to prevent it from being displayed
51134      */
51135     disable : function(){
51136        this.disabled = true;
51137     },
51138
51139     /**
51140      * Enables the mask so that it can be displayed
51141      */
51142     enable : function(){
51143         this.disabled = false;
51144     },
51145
51146     // private
51147     onLoad : function(){
51148         this.el.unmask(this.removeMask);
51149     },
51150
51151     // private
51152     onBeforeLoad : function(){
51153         if(!this.disabled){
51154             this.el.mask(this.msg, this.msgCls);
51155         }
51156     },
51157
51158     // private
51159     destroy : function(){
51160         if(this.store){
51161             this.store.un('beforeload', this.onBeforeLoad, this);
51162             this.store.un('load', this.onLoad, this);
51163             this.store.un('loadexception', this.onLoad, this);
51164         }else{
51165             var um = this.el.getUpdateManager();
51166             um.un('beforeupdate', this.onBeforeLoad, this);
51167             um.un('update', this.onLoad, this);
51168             um.un('failure', this.onLoad, this);
51169         }
51170     }
51171 };/*
51172  * Based on:
51173  * Ext JS Library 1.1.1
51174  * Copyright(c) 2006-2007, Ext JS, LLC.
51175  *
51176  * Originally Released Under LGPL - original licence link has changed is not relivant.
51177  *
51178  * Fork - LGPL
51179  * <script type="text/javascript">
51180  */
51181 Roo.XTemplate = function(){
51182     Roo.XTemplate.superclass.constructor.apply(this, arguments);
51183     var s = this.html;
51184
51185     s = ['<tpl>', s, '</tpl>'].join('');
51186
51187     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
51188
51189     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
51190     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
51191     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
51192     var m, id = 0;
51193     var tpls = [];
51194
51195     while(m = s.match(re)){
51196        var m2 = m[0].match(nameRe);
51197        var m3 = m[0].match(ifRe);
51198        var m4 = m[0].match(execRe);
51199        var exp = null, fn = null, exec = null;
51200        var name = m2 && m2[1] ? m2[1] : '';
51201        if(m3){
51202            exp = m3 && m3[1] ? m3[1] : null;
51203            if(exp){
51204                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
51205            }
51206        }
51207        if(m4){
51208            exp = m4 && m4[1] ? m4[1] : null;
51209            if(exp){
51210                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
51211            }
51212        }
51213        if(name){
51214            switch(name){
51215                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
51216                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
51217                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
51218            }
51219        }
51220        tpls.push({
51221             id: id,
51222             target: name,
51223             exec: exec,
51224             test: fn,
51225             body: m[1]||''
51226         });
51227        s = s.replace(m[0], '{xtpl'+ id + '}');
51228        ++id;
51229     }
51230     for(var i = tpls.length-1; i >= 0; --i){
51231         this.compileTpl(tpls[i]);
51232     }
51233     this.master = tpls[tpls.length-1];
51234     this.tpls = tpls;
51235 };
51236 Roo.extend(Roo.XTemplate, Roo.Template, {
51237
51238     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
51239
51240     applySubTemplate : function(id, values, parent){
51241         var t = this.tpls[id];
51242         if(t.test && !t.test.call(this, values, parent)){
51243             return '';
51244         }
51245         if(t.exec && t.exec.call(this, values, parent)){
51246             return '';
51247         }
51248         var vs = t.target ? t.target.call(this, values, parent) : values;
51249         parent = t.target ? values : parent;
51250         if(t.target && vs instanceof Array){
51251             var buf = [];
51252             for(var i = 0, len = vs.length; i < len; i++){
51253                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
51254             }
51255             return buf.join('');
51256         }
51257         return t.compiled.call(this, vs, parent);
51258     },
51259
51260     compileTpl : function(tpl){
51261         var fm = Roo.util.Format;
51262         var useF = this.disableFormats !== true;
51263         var sep = Roo.isGecko ? "+" : ",";
51264         var fn = function(m, name, format, args){
51265             if(name.substr(0, 4) == 'xtpl'){
51266                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
51267             }
51268             var v;
51269             if(name.indexOf('.') != -1){
51270                 v = name;
51271             }else{
51272                 v = "values['" + name + "']";
51273             }
51274             if(format && useF){
51275                 args = args ? ',' + args : "";
51276                 if(format.substr(0, 5) != "this."){
51277                     format = "fm." + format + '(';
51278                 }else{
51279                     format = 'this.call("'+ format.substr(5) + '", ';
51280                     args = ", values";
51281                 }
51282             }else{
51283                 args= ''; format = "("+v+" === undefined ? '' : ";
51284             }
51285             return "'"+ sep + format + v + args + ")"+sep+"'";
51286         };
51287         var body;
51288         // branched to use + in gecko and [].join() in others
51289         if(Roo.isGecko){
51290             body = "tpl.compiled = function(values, parent){ return '" +
51291                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
51292                     "';};";
51293         }else{
51294             body = ["tpl.compiled = function(values, parent){ return ['"];
51295             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
51296             body.push("'].join('');};");
51297             body = body.join('');
51298         }
51299         /** eval:var:zzzzzzz */
51300         eval(body);
51301         return this;
51302     },
51303
51304     applyTemplate : function(values){
51305         return this.master.compiled.call(this, values, {});
51306         var s = this.subs;
51307     },
51308
51309     apply : function(){
51310         return this.applyTemplate.apply(this, arguments);
51311     },
51312
51313     compile : function(){return this;}
51314 });
51315
51316 Roo.XTemplate.from = function(el){
51317     el = Roo.getDom(el);
51318     return new Roo.XTemplate(el.value || el.innerHTML);
51319 };/*
51320  * Original code for Roojs - LGPL
51321  * <script type="text/javascript">
51322  */
51323  
51324 /**
51325  * @class Roo.XComponent
51326  * A delayed Element creator...
51327  * Or a way to group chunks of interface together.
51328  * 
51329  * Mypart.xyx = new Roo.XComponent({
51330
51331     parent : 'Mypart.xyz', // empty == document.element.!!
51332     order : '001',
51333     name : 'xxxx'
51334     region : 'xxxx'
51335     disabled : function() {} 
51336      
51337     tree : function() { // return an tree of xtype declared components
51338         var MODULE = this;
51339         return 
51340         {
51341             xtype : 'NestedLayoutPanel',
51342             // technicall
51343         }
51344      ]
51345  *})
51346  *
51347  *
51348  * It can be used to build a big heiracy, with parent etc.
51349  * or you can just use this to render a single compoent to a dom element
51350  * MYPART.render(Roo.Element | String(id) | dom_element )
51351  * 
51352  * @extends Roo.util.Observable
51353  * @constructor
51354  * @param cfg {Object} configuration of component
51355  * 
51356  */
51357 Roo.XComponent = function(cfg) {
51358     Roo.apply(this, cfg);
51359     this.addEvents({ 
51360         /**
51361              * @event built
51362              * Fires when this the componnt is built
51363              * @param {Roo.XComponent} c the component
51364              */
51365         'built' : true,
51366         /**
51367              * @event buildcomplete
51368              * Fires on the top level element when all elements have been built
51369              * @param {Roo.XComponent} c the top level component.
51370          */
51371         'buildcomplete' : true
51372         
51373     });
51374     this.region = this.region || 'center'; // default..
51375     Roo.XComponent.register(this);
51376     this.modules = false;
51377     this.el = false; // where the layout goes..
51378     
51379     
51380 }
51381 Roo.extend(Roo.XComponent, Roo.util.Observable, {
51382     /**
51383      * @property el
51384      * The created element (with Roo.factory())
51385      * @type {Roo.Layout}
51386      */
51387     el  : false,
51388     
51389     /**
51390      * @property el
51391      * for BC  - use el in new code
51392      * @type {Roo.Layout}
51393      */
51394     panel : false,
51395     
51396     /**
51397      * @property layout
51398      * for BC  - use el in new code
51399      * @type {Roo.Layout}
51400      */
51401     layout : false,
51402     
51403      /**
51404      * @cfg {Function|boolean} disabled
51405      * If this module is disabled by some rule, return true from the funtion
51406      */
51407     disabled : false,
51408     
51409     /**
51410      * @cfg {String} parent 
51411      * Name of parent element which it get xtype added to..
51412      */
51413     parent: false,
51414     
51415     /**
51416      * @cfg {String} order
51417      * Used to set the order in which elements are created (usefull for multiple tabs)
51418      */
51419     
51420     order : false,
51421     /**
51422      * @cfg {String} name
51423      * String to display while loading.
51424      */
51425     name : false,
51426     /**
51427      * @cfg {String} region
51428      * Region to render component to (defaults to center)
51429      */
51430     region : 'center',
51431     
51432     /**
51433      * @cfg {Array} items
51434      * A single item array - the first element is the root of the tree..
51435      * It's done this way to stay compatible with the Xtype system...
51436      */
51437     items : false,
51438     
51439     
51440      /**
51441      * render
51442      * render element to dom or tree
51443      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
51444      */
51445     
51446     render : function(el)
51447     {
51448         
51449         el = el || false;
51450         var hp = this.parent ? 1 : 0;
51451         
51452         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
51453             // if parent is a '#.....' string, then let's use that..
51454             var ename = this.parent.substr(1)
51455             this.parent = false;
51456             el = Roo.get(ename);
51457             if (!el) {
51458                 Roo.log("Warning - element can not be found :#" + ename );
51459                 return;
51460             }
51461         }
51462         
51463         
51464         if (!this.parent) {
51465             
51466             el = el ? Roo.get(el) : false;
51467             
51468             // it's a top level one..
51469             this.parent =  {
51470                 el : new Roo.BorderLayout(el || document.body, {
51471                 
51472                      center: {
51473                          titlebar: false,
51474                          autoScroll:false,
51475                          closeOnTab: true,
51476                          tabPosition: 'top',
51477                           //resizeTabs: true,
51478                          alwaysShowTabs: el && hp? false :  true,
51479                          hideTabs: el || !hp ? true :  false,
51480                          minTabWidth: 140
51481                      }
51482                  })
51483             }
51484         }
51485         
51486         
51487             
51488         var tree = this.tree();
51489         tree.region = tree.region || this.region;
51490         this.el = this.parent.el.addxtype(tree);
51491         this.fireEvent('built', this);
51492         
51493         this.panel = this.el;
51494         this.layout = this.panel.layout;    
51495          
51496     }
51497     
51498 });
51499
51500 Roo.apply(Roo.XComponent, {
51501     
51502     /**
51503      * @property  buildCompleted
51504      * True when the builder has completed building the interface.
51505      * @type Boolean
51506      */
51507     buildCompleted : false,
51508      
51509     /**
51510      * @property  topModule
51511      * the upper most module - uses document.element as it's constructor.
51512      * @type Object
51513      */
51514      
51515     topModule  : false,
51516       
51517     /**
51518      * @property  modules
51519      * array of modules to be created by registration system.
51520      * @type {Array} of Roo.XComponent
51521      */
51522     
51523     modules : [],
51524     /**
51525      * @property  elmodules
51526      * array of modules to be created by which use #ID 
51527      * @type {Array} of Roo.XComponent
51528      */
51529      
51530     elmodules : [],
51531
51532     
51533     /**
51534      * Register components to be built later.
51535      *
51536      * This solves the following issues
51537      * - Building is not done on page load, but after an authentication process has occured.
51538      * - Interface elements are registered on page load
51539      * - Parent Interface elements may not be loaded before child, so this handles that..
51540      * 
51541      *
51542      * example:
51543      * 
51544      * MyApp.register({
51545           order : '000001',
51546           module : 'Pman.Tab.projectMgr',
51547           region : 'center',
51548           parent : 'Pman.layout',
51549           disabled : false,  // or use a function..
51550         })
51551      
51552      * * @param {Object} details about module
51553      */
51554     register : function(obj) {
51555         this.modules.push(obj);
51556          
51557     },
51558     /**
51559      * convert a string to an object..
51560      * eg. 'AAA.BBB' -> finds AAA.BBB
51561
51562      */
51563     
51564     toObject : function(str)
51565     {
51566         if (!str || typeof(str) == 'object') {
51567             return str;
51568         }
51569         if (str.substring(0,1) == '#') {
51570             return str;
51571         }
51572
51573         var ar = str.split('.');
51574         var rt, o;
51575         rt = ar.shift();
51576             /** eval:var:o */
51577         try {
51578             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
51579         } catch (e) {
51580             throw "Module not found : " + str;
51581         }
51582         
51583         if (o === false) {
51584             throw "Module not found : " + str;
51585         }
51586         Roo.each(ar, function(e) {
51587             if (typeof(o[e]) == 'undefined') {
51588                 throw "Module not found : " + str;
51589             }
51590             o = o[e];
51591         });
51592         
51593         return o;
51594         
51595     },
51596     
51597     
51598     /**
51599      * move modules into their correct place in the tree..
51600      * 
51601      */
51602     preBuild : function ()
51603     {
51604         var _t = this;
51605         Roo.each(this.modules , function (obj)
51606         {
51607             var opar = obj.parent;
51608             try { 
51609                 obj.parent = this.toObject(opar);
51610             } catch(e) {
51611                 Roo.log(e.toString());
51612                 return;
51613             }
51614             
51615             if (!obj.parent) {
51616                 this.topModule = obj;
51617                 return;
51618             }
51619             if (typeof(obj.parent) == 'string') {
51620                 this.elmodules.push(obj);
51621                 return;
51622             }
51623             if (obj.parent.constructor != Roo.XComponent) {
51624                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
51625             }
51626             if (!obj.parent.modules) {
51627                 obj.parent.modules = new Roo.util.MixedCollection(false, 
51628                     function(o) { return o.order + '' }
51629                 );
51630             }
51631             
51632             obj.parent.modules.add(obj);
51633         }, this);
51634     },
51635     
51636      /**
51637      * make a list of modules to build.
51638      * @return {Array} list of modules. 
51639      */ 
51640     
51641     buildOrder : function()
51642     {
51643         var _this = this;
51644         var cmp = function(a,b) {   
51645             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
51646         };
51647         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
51648             throw "No top level modules to build";
51649         }
51650         
51651         // make a flat list in order of modules to build.
51652         var mods = this.topModule ? [ this.topModule ] : [];
51653         Roo.each(this.elmodules,function(e) { mods.push(e) });
51654
51655         
51656         // add modules to their parents..
51657         var addMod = function(m) {
51658            // Roo.debug && Roo.log(m.modKey);
51659             
51660             mods.push(m);
51661             if (m.modules) {
51662                 m.modules.keySort('ASC',  cmp );
51663                 m.modules.each(addMod);
51664             }
51665             // not sure if this is used any more..
51666             if (m.finalize) {
51667                 m.finalize.name = m.name + " (clean up) ";
51668                 mods.push(m.finalize);
51669             }
51670             
51671         }
51672         if (this.topModule) { 
51673             this.topModule.modules.keySort('ASC',  cmp );
51674             this.topModule.modules.each(addMod);
51675         }
51676         return mods;
51677     },
51678     
51679      /**
51680      * Build the registered modules.
51681      * @param {Object} parent element.
51682      * @param {Function} optional method to call after module has been added.
51683      * 
51684      */ 
51685    
51686     build : function() 
51687     {
51688         
51689         this.preBuild();
51690         var mods = this.buildOrder();
51691       
51692         //this.allmods = mods;
51693         //Roo.debug && Roo.log(mods);
51694         //return;
51695         if (!mods.length) { // should not happen
51696             throw "NO modules!!!";
51697         }
51698         
51699         
51700         
51701         // flash it up as modal - so we store the mask!?
51702         Roo.MessageBox.show({ title: 'loading' });
51703         Roo.MessageBox.show({
51704            title: "Please wait...",
51705            msg: "Building Interface...",
51706            width:450,
51707            progress:true,
51708            closable:false,
51709            modal: false
51710           
51711         });
51712         var total = mods.length;
51713         
51714         var _this = this;
51715         var progressRun = function() {
51716             if (!mods.length) {
51717                 Roo.debug && Roo.log('hide?');
51718                 Roo.MessageBox.hide();
51719                 if (_this.topModule) { 
51720                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
51721                 }
51722                 // THE END...
51723                 return false;   
51724             }
51725             
51726             var m = mods.shift();
51727             
51728             
51729             Roo.debug && Roo.log(m);
51730             // not sure if this is supported any more.. - modules that are are just function
51731             if (typeof(m) == 'function') { 
51732                 m.call(this);
51733                 return progressRun.defer(10, _this);
51734             } 
51735             
51736             
51737             
51738             Roo.MessageBox.updateProgress(
51739                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
51740                     " of " + total + 
51741                     (m.name ? (' - ' + m.name) : '')
51742                     );
51743             
51744          
51745             // is the module disabled?
51746             var disabled = (typeof(m.disabled) == 'function') ?
51747                 m.disabled.call(m.module.disabled) : m.disabled;    
51748             
51749             
51750             if (disabled) {
51751                 return progressRun(); // we do not update the display!
51752             }
51753             
51754             // now build 
51755             
51756             m.render();
51757             // it's 10 on top level, and 1 on others??? why...
51758             return progressRun.defer(10, _this);
51759              
51760         }
51761         progressRun.defer(1, _this);
51762      
51763         
51764         
51765     }
51766     
51767      
51768    
51769     
51770     
51771 });
51772  //<script type="text/javascript">
51773
51774
51775 /**
51776  * @class Roo.Login
51777  * @extends Roo.LayoutDialog
51778  * A generic Login Dialog..... - only one needed in theory!?!?
51779  *
51780  * Fires XComponent builder on success...
51781  * 
51782  * Sends 
51783  *    username,password, lang = for login actions.
51784  *    check = 1 for periodic checking that sesion is valid.
51785  *    passwordRequest = email request password
51786  *    logout = 1 = to logout
51787  * 
51788  * Affects: (this id="????" elements)
51789  *   loading  (removed) (used to indicate application is loading)
51790  *   loading-mask (hides) (used to hide application when it's building loading)
51791  *   
51792  * 
51793  * Usage: 
51794  *    
51795  * 
51796  * Myapp.login = Roo.Login({
51797      url: xxxx,
51798    
51799      realm : 'Myapp', 
51800      
51801      
51802      method : 'POST',
51803      
51804      
51805      * 
51806  })
51807  * 
51808  * 
51809  * 
51810  **/
51811  
51812 Roo.Login = function(cfg)
51813 {
51814     this.addEvents({
51815         'refreshed' : true
51816     });
51817     
51818     Roo.apply(this,cfg);
51819     
51820     Roo.onReady(function() {
51821         this.onLoad();
51822     }, this);
51823     // call parent..
51824     
51825    
51826     Roo.Login.superclass.constructor.call(this, this);
51827     //this.addxtype(this.items[0]);
51828     
51829     
51830 }
51831
51832
51833 Roo.extend(Roo.Login, Roo.LayoutDialog, {
51834     
51835     /**
51836      * @cfg {String} method
51837      * Method used to query for login details.
51838      */
51839     
51840     method : 'POST',
51841     /**
51842      * @cfg {String} url
51843      * URL to query login data. - eg. baseURL + '/Login.php'
51844      */
51845     url : '',
51846     
51847     /**
51848      * @property user
51849      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
51850      * @type {Object} 
51851      */
51852     user : false,
51853     /**
51854      * @property checkFails
51855      * Number of times we have attempted to get authentication check, and failed.
51856      * @type {Number} 
51857      */
51858     checkFails : 0,
51859       /**
51860      * @property intervalID
51861      * The window interval that does the constant login checking.
51862      * @type {Number} 
51863      */
51864     intervalID : 0,
51865     
51866     
51867     onLoad : function() // called on page load...
51868     {
51869         // load 
51870          
51871         if (Roo.get('loading')) { // clear any loading indicator..
51872             Roo.get('loading').remove();
51873         }
51874         
51875         //this.switchLang('en'); // set the language to english..
51876        
51877         this.check({
51878             success:  function(response, opts)  {  // check successfull...
51879             
51880                 var res = this.processResponse(response);
51881                 this.checkFails =0;
51882                 if (!res.success) { // error!
51883                     this.checkFails = 5;
51884                     //console.log('call failure');
51885                     return this.failure(response,opts);
51886                 }
51887                 
51888                 if (!res.data.id) { // id=0 == login failure.
51889                     return this.show();
51890                 }
51891                 
51892                               
51893                         //console.log(success);
51894                 this.fillAuth(res.data);   
51895                 this.checkFails =0;
51896                 Roo.XComponent.build();
51897             },
51898             failure : this.show
51899         });
51900         
51901     }, 
51902     
51903     
51904     check: function(cfg) // called every so often to refresh cookie etc..
51905     {
51906         if (cfg.again) { // could be undefined..
51907             this.checkFails++;
51908         } else {
51909             this.checkFails = 0;
51910         }
51911         var _this = this;
51912         if (this.sending) {
51913             if ( this.checkFails > 4) {
51914                 Roo.MessageBox.alert("Error",  
51915                     "Error getting authentication status. - try reloading, or wait a while", function() {
51916                         _this.sending = false;
51917                     }); 
51918                 return;
51919             }
51920             cfg.again = true;
51921             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
51922             return;
51923         }
51924         this.sending = true;
51925         
51926         Roo.Ajax.request({  
51927             url: this.url,
51928             params: {
51929                 getAuthUser: true
51930             },  
51931             method: this.method,
51932             success:  cfg.success || this.success,
51933             failure : cfg.failure || this.failure,
51934             scope : this,
51935             callCfg : cfg
51936               
51937         });  
51938     }, 
51939     
51940     
51941     logout: function()
51942     {
51943         window.onbeforeunload = function() { }; // false does not work for IE..
51944         this.user = false;
51945         var _this = this;
51946         
51947         Roo.Ajax.request({  
51948             url: this.url,
51949             params: {
51950                 logout: 1
51951             },  
51952             method: 'GET',
51953             failure : function() {
51954                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
51955                     document.location = document.location.toString() + '?ts=' + Math.random();
51956                 });
51957                 
51958             },
51959             success : function() {
51960                 _this.user = false;
51961                 this.checkFails =0;
51962                 // fixme..
51963                 document.location = document.location.toString() + '?ts=' + Math.random();
51964             }
51965               
51966               
51967         }); 
51968     },
51969     
51970     processResponse : function (response)
51971     {
51972         var res = '';
51973         try {
51974             res = Roo.decode(response.responseText);
51975             // oops...
51976             if (typeof(res) != 'object') {
51977                 res = { success : false, errorMsg : res, errors : true };
51978             }
51979             if (typeof(res.success) == 'undefined') {
51980                 res.success = false;
51981             }
51982             
51983         } catch(e) {
51984             res = { success : false,  errorMsg : response.responseText, errors : true };
51985         }
51986         return res;
51987     },
51988     
51989     success : function(response, opts)  // check successfull...
51990     {  
51991         this.sending = false;
51992         var res = this.processResponse(response);
51993         if (!res.success) {
51994             return this.failure(response, opts);
51995         }
51996         if (!res.data || !res.data.id) {
51997             return this.failure(response,opts);
51998         }
51999         //console.log(res);
52000         this.fillAuth(res.data);
52001         
52002         this.checkFails =0;
52003         
52004     },
52005     
52006     
52007     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
52008     {
52009         this.authUser = -1;
52010         this.sending = false;
52011         var res = this.processResponse(response);
52012         //console.log(res);
52013         if ( this.checkFails > 2) {
52014         
52015             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
52016                 "Error getting authentication status. - try reloading"); 
52017             return;
52018         }
52019         opts.callCfg.again = true;
52020         this.check.defer(1000, this, [ opts.callCfg ]);
52021         return;  
52022     },
52023     
52024     
52025     
52026     fillAuth: function(au) {
52027         this.startAuthCheck();
52028         this.authUserId = au.id;
52029         this.authUser = au;
52030         this.lastChecked = new Date();
52031         this.fireEvent('refreshed', au);
52032         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
52033         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
52034         au.lang = au.lang || 'en';
52035         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
52036         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
52037         this.switchLang(au.lang );
52038         
52039      
52040         // open system... - -on setyp..
52041         if (this.authUserId  < 0) {
52042             Roo.MessageBox.alert("Warning", 
52043                 "This is an open system - please set up a admin user with a password.");  
52044         }
52045          
52046         //Pman.onload(); // which should do nothing if it's a re-auth result...
52047         
52048              
52049     },
52050     
52051     startAuthCheck : function() // starter for timeout checking..
52052     {
52053         if (this.intervalID) { // timer already in place...
52054             return false;
52055         }
52056         var _this = this;
52057         this.intervalID =  window.setInterval(function() {
52058               _this.check(false);
52059             }, 120000); // every 120 secs = 2mins..
52060         
52061         
52062     },
52063          
52064     
52065     switchLang : function (lang) 
52066     {
52067         _T = typeof(_T) == 'undefined' ? false : _T;
52068           if (!_T || !lang.length) {
52069             return;
52070         }
52071         
52072         if (!_T && lang != 'en') {
52073             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52074             return;
52075         }
52076         
52077         if (typeof(_T.en) == 'undefined') {
52078             _T.en = {};
52079             Roo.apply(_T.en, _T);
52080         }
52081         
52082         if (typeof(_T[lang]) == 'undefined') {
52083             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52084             return;
52085         }
52086         
52087         
52088         Roo.apply(_T, _T[lang]);
52089         // just need to set the text values for everything...
52090         var _this = this;
52091         /* this will not work ...
52092         if (this.form) { 
52093             
52094                
52095             function formLabel(name, val) {
52096                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
52097             }
52098             
52099             formLabel('password', "Password"+':');
52100             formLabel('username', "Email Address"+':');
52101             formLabel('lang', "Language"+':');
52102             this.dialog.setTitle("Login");
52103             this.dialog.buttons[0].setText("Forgot Password");
52104             this.dialog.buttons[1].setText("Login");
52105         }
52106         */
52107         
52108         
52109     },
52110     
52111     
52112     title: "Login",
52113     modal: true,
52114     width:  350,
52115     //height: 230,
52116     height: 180,
52117     shadow: true,
52118     minWidth:200,
52119     minHeight:180,
52120     //proxyDrag: true,
52121     closable: false,
52122     draggable: false,
52123     collapsible: false,
52124     resizable: false,
52125     center: {  // needed??
52126         autoScroll:false,
52127         titlebar: false,
52128        // tabPosition: 'top',
52129         hideTabs: true,
52130         closeOnTab: true,
52131         alwaysShowTabs: false
52132     } ,
52133     listeners : {
52134         
52135         show  : function(dlg)
52136         {
52137             //console.log(this);
52138             this.form = this.layout.getRegion('center').activePanel.form;
52139             this.form.dialog = dlg;
52140             this.buttons[0].form = this.form;
52141             this.buttons[0].dialog = dlg;
52142             this.buttons[1].form = this.form;
52143             this.buttons[1].dialog = dlg;
52144            
52145            //this.resizeToLogo.defer(1000,this);
52146             // this is all related to resizing for logos..
52147             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
52148            //// if (!sz) {
52149              //   this.resizeToLogo.defer(1000,this);
52150              //   return;
52151            // }
52152             //var w = Ext.lib.Dom.getViewWidth() - 100;
52153             //var h = Ext.lib.Dom.getViewHeight() - 100;
52154             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
52155             //this.center();
52156             if (this.disabled) {
52157                 this.hide();
52158                 return;
52159             }
52160             
52161             if (this.user.id < 0) { // used for inital setup situations.
52162                 return;
52163             }
52164             
52165             if (this.intervalID) {
52166                 // remove the timer
52167                 window.clearInterval(this.intervalID);
52168                 this.intervalID = false;
52169             }
52170             
52171             
52172             if (Roo.get('loading')) {
52173                 Roo.get('loading').remove();
52174             }
52175             if (Roo.get('loading-mask')) {
52176                 Roo.get('loading-mask').hide();
52177             }
52178             
52179             //incomming._node = tnode;
52180             this.form.reset();
52181             //this.dialog.modal = !modal;
52182             //this.dialog.show();
52183             this.el.unmask(); 
52184             
52185             
52186             this.form.setValues({
52187                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
52188                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
52189             });
52190             
52191             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
52192             if (this.form.findField('username').getValue().length > 0 ){
52193                 this.form.findField('password').focus();
52194             } else {
52195                this.form.findField('username').focus();
52196             }
52197     
52198         }
52199     },
52200     items : [
52201          {
52202        
52203             xtype : 'ContentPanel',
52204             xns : Roo,
52205             region: 'center',
52206             fitToFrame : true,
52207             
52208             items : [
52209     
52210                 {
52211                
52212                     xtype : 'Form',
52213                     xns : Roo.form,
52214                     labelWidth: 100,
52215                     style : 'margin: 10px;',
52216                     
52217                     listeners : {
52218                         actionfailed : function(f, act) {
52219                             // form can return { errors: .... }
52220                                 
52221                             //act.result.errors // invalid form element list...
52222                             //act.result.errorMsg// invalid form element list...
52223                             
52224                             this.dialog.el.unmask();
52225                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
52226                                         "Login failed - communication error - try again.");
52227                                       
52228                         },
52229                         actioncomplete: function(re, act) {
52230                              
52231                             Roo.state.Manager.set(
52232                                 this.dialog.realm + '.username',  
52233                                     this.findField('username').getValue()
52234                             );
52235                             Roo.state.Manager.set(
52236                                 this.dialog.realm + '.lang',  
52237                                 this.findField('lang').getValue() 
52238                             );
52239                             
52240                             this.dialog.fillAuth(act.result.data);
52241                               
52242                             this.dialog.hide();
52243                             
52244                             if (Roo.get('loading-mask')) {
52245                                 Roo.get('loading-mask').show();
52246                             }
52247                             Roo.XComponent.build();
52248                             
52249                              
52250                             
52251                         }
52252                     },
52253                     items : [
52254                         {
52255                             xtype : 'TextField',
52256                             xns : Roo.form,
52257                             fieldLabel: "Email Address",
52258                             name: 'username',
52259                             width:200,
52260                             autoCreate : {tag: "input", type: "text", size: "20"}
52261                         },
52262                         {
52263                             xtype : 'TextField',
52264                             xns : Roo.form,
52265                             fieldLabel: "Password",
52266                             inputType: 'password',
52267                             name: 'password',
52268                             width:200,
52269                             autoCreate : {tag: "input", type: "text", size: "20"},
52270                             listeners : {
52271                                 specialkey : function(e,ev) {
52272                                     if (ev.keyCode == 13) {
52273                                         this.form.dialog.el.mask("Logging in");
52274                                         this.form.doAction('submit', {
52275                                             url: this.form.dialog.url,
52276                                             method: this.form.dialog.method
52277                                         });
52278                                     }
52279                                 }
52280                             }  
52281                         },
52282                         {
52283                             xtype : 'ComboBox',
52284                             xns : Roo.form,
52285                             fieldLabel: "Language",
52286                             name : 'langdisp',
52287                             store: {
52288                                 xtype : 'SimpleStore',
52289                                 fields: ['lang', 'ldisp'],
52290                                 data : [
52291                                     [ 'en', 'English' ],
52292                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
52293                                     [ 'zh_CN', '\u7C21\u4E2D' ]
52294                                 ]
52295                             },
52296                             
52297                             valueField : 'lang',
52298                             hiddenName:  'lang',
52299                             width: 200,
52300                             displayField:'ldisp',
52301                             typeAhead: false,
52302                             editable: false,
52303                             mode: 'local',
52304                             triggerAction: 'all',
52305                             emptyText:'Select a Language...',
52306                             selectOnFocus:true,
52307                             listeners : {
52308                                 select :  function(cb, rec, ix) {
52309                                     this.form.switchLang(rec.data.lang);
52310                                 }
52311                             }
52312                         
52313                         }
52314                     ]
52315                 }
52316                   
52317                 
52318             ]
52319         }
52320     ],
52321     buttons : [
52322         {
52323             xtype : 'Button',
52324             xns : 'Roo',
52325             text : "Forgot Password",
52326             listeners : {
52327                 click : function() {
52328                     //console.log(this);
52329                     var n = this.form.findField('username').getValue();
52330                     if (!n.length) {
52331                         Roo.MessageBox.alert("Error", "Fill in your email address");
52332                         return;
52333                     }
52334                     Roo.Ajax.request({
52335                         url: this.dialog.url,
52336                         params: {
52337                             passwordRequest: n
52338                         },
52339                         method: this.dialog.method,
52340                         success:  function(response, opts)  {  // check successfull...
52341                         
52342                             var res = this.dialog.processResponse(response);
52343                             if (!res.success) { // error!
52344                                Roo.MessageBox.alert("Error" ,
52345                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
52346                                return;
52347                             }
52348                             Roo.MessageBox.alert("Notice" ,
52349                                 "Please check you email for the Password Reset message");
52350                         },
52351                         failure : function() {
52352                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
52353                         }
52354                         
52355                     });
52356                 }
52357             }
52358         },
52359         {
52360             xtype : 'Button',
52361             xns : 'Roo',
52362             text : "Login",
52363             listeners : {
52364                 
52365                 click : function () {
52366                         
52367                     this.dialog.el.mask("Logging in");
52368                     this.form.doAction('submit', {
52369                             url: this.dialog.url,
52370                             method: this.dialog.method
52371                     });
52372                 }
52373             }
52374         }
52375     ]
52376   
52377   
52378 })
52379  
52380
52381
52382