roojs-debug.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours
1010   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1011   T      CST        Timezone setting of the machine running the code
1012   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1013 </pre>
1014  *
1015  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1016  * <pre><code>
1017 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1018 document.write(dt.format('Y-m-d'));                         //2007-01-10
1019 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1020 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1021  </code></pre>
1022  *
1023  * Here are some standard date/time patterns that you might find helpful.  They
1024  * are not part of the source of Date.js, but to use them you can simply copy this
1025  * block of code into any script that is included after Date.js and they will also become
1026  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1027  * <pre><code>
1028 Date.patterns = {
1029     ISO8601Long:"Y-m-d H:i:s",
1030     ISO8601Short:"Y-m-d",
1031     ShortDate: "n/j/Y",
1032     LongDate: "l, F d, Y",
1033     FullDateTime: "l, F d, Y g:i:s A",
1034     MonthDay: "F d",
1035     ShortTime: "g:i A",
1036     LongTime: "g:i:s A",
1037     SortableDateTime: "Y-m-d\\TH:i:s",
1038     UniversalSortableDateTime: "Y-m-d H:i:sO",
1039     YearMonth: "F, Y"
1040 };
1041 </code></pre>
1042  *
1043  * Example usage:
1044  * <pre><code>
1045 var dt = new Date();
1046 document.write(dt.format(Date.patterns.ShortDate));
1047  </code></pre>
1048  */
1049
1050 /*
1051  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1052  * They generate precompiled functions from date formats instead of parsing and
1053  * processing the pattern every time you format a date.  These functions are available
1054  * on every Date object (any javascript function).
1055  *
1056  * The original article and download are here:
1057  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1058  *
1059  */
1060  
1061  
1062  // was in core
1063 /**
1064  Returns the number of milliseconds between this date and date
1065  @param {Date} date (optional) Defaults to now
1066  @return {Number} The diff in milliseconds
1067  @member Date getElapsed
1068  */
1069 Date.prototype.getElapsed = function(date) {
1070         return Math.abs((date || new Date()).getTime()-this.getTime());
1071 };
1072 // was in date file..
1073
1074
1075 // private
1076 Date.parseFunctions = {count:0};
1077 // private
1078 Date.parseRegexes = [];
1079 // private
1080 Date.formatFunctions = {count:0};
1081
1082 // private
1083 Date.prototype.dateFormat = function(format) {
1084     if (Date.formatFunctions[format] == null) {
1085         Date.createNewFormat(format);
1086     }
1087     var func = Date.formatFunctions[format];
1088     return this[func]();
1089 };
1090
1091
1092 /**
1093  * Formats a date given the supplied format string
1094  * @param {String} format The format string
1095  * @return {String} The formatted date
1096  * @method
1097  */
1098 Date.prototype.format = Date.prototype.dateFormat;
1099
1100 // private
1101 Date.createNewFormat = function(format) {
1102     var funcName = "format" + Date.formatFunctions.count++;
1103     Date.formatFunctions[format] = funcName;
1104     var code = "Date.prototype." + funcName + " = function(){return ";
1105     var special = false;
1106     var ch = '';
1107     for (var i = 0; i < format.length; ++i) {
1108         ch = format.charAt(i);
1109         if (!special && ch == "\\") {
1110             special = true;
1111         }
1112         else if (special) {
1113             special = false;
1114             code += "'" + String.escape(ch) + "' + ";
1115         }
1116         else {
1117             code += Date.getFormatCode(ch);
1118         }
1119     }
1120     /** eval:var:zzzzzzzzzzzzz */
1121     eval(code.substring(0, code.length - 3) + ";}");
1122 };
1123
1124 // private
1125 Date.getFormatCode = function(character) {
1126     switch (character) {
1127     case "d":
1128         return "String.leftPad(this.getDate(), 2, '0') + ";
1129     case "D":
1130         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1131     case "j":
1132         return "this.getDate() + ";
1133     case "l":
1134         return "Date.dayNames[this.getDay()] + ";
1135     case "S":
1136         return "this.getSuffix() + ";
1137     case "w":
1138         return "this.getDay() + ";
1139     case "z":
1140         return "this.getDayOfYear() + ";
1141     case "W":
1142         return "this.getWeekOfYear() + ";
1143     case "F":
1144         return "Date.monthNames[this.getMonth()] + ";
1145     case "m":
1146         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1147     case "M":
1148         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1149     case "n":
1150         return "(this.getMonth() + 1) + ";
1151     case "t":
1152         return "this.getDaysInMonth() + ";
1153     case "L":
1154         return "(this.isLeapYear() ? 1 : 0) + ";
1155     case "Y":
1156         return "this.getFullYear() + ";
1157     case "y":
1158         return "('' + this.getFullYear()).substring(2, 4) + ";
1159     case "a":
1160         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1161     case "A":
1162         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1163     case "g":
1164         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1165     case "G":
1166         return "this.getHours() + ";
1167     case "h":
1168         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1169     case "H":
1170         return "String.leftPad(this.getHours(), 2, '0') + ";
1171     case "i":
1172         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1173     case "s":
1174         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1175     case "O":
1176         return "this.getGMTOffset() + ";
1177     case "P":
1178         return "this.getGMTColonOffset() + ";
1179     case "T":
1180         return "this.getTimezone() + ";
1181     case "Z":
1182         return "(this.getTimezoneOffset() * -60) + ";
1183     default:
1184         return "'" + String.escape(character) + "' + ";
1185     }
1186 };
1187
1188 /**
1189  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1190  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1191  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1192  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1193  * string or the parse operation will fail.
1194  * Example Usage:
1195 <pre><code>
1196 //dt = Fri May 25 2007 (current date)
1197 var dt = new Date();
1198
1199 //dt = Thu May 25 2006 (today's month/day in 2006)
1200 dt = Date.parseDate("2006", "Y");
1201
1202 //dt = Sun Jan 15 2006 (all date parts specified)
1203 dt = Date.parseDate("2006-1-15", "Y-m-d");
1204
1205 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1206 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1207 </code></pre>
1208  * @param {String} input The unparsed date as a string
1209  * @param {String} format The format the date is in
1210  * @return {Date} The parsed date
1211  * @static
1212  */
1213 Date.parseDate = function(input, format) {
1214     if (Date.parseFunctions[format] == null) {
1215         Date.createParser(format);
1216     }
1217     var func = Date.parseFunctions[format];
1218     return Date[func](input);
1219 };
1220 /**
1221  * @private
1222  */
1223 Date.createParser = function(format) {
1224     var funcName = "parse" + Date.parseFunctions.count++;
1225     var regexNum = Date.parseRegexes.length;
1226     var currentGroup = 1;
1227     Date.parseFunctions[format] = funcName;
1228
1229     var code = "Date." + funcName + " = function(input){\n"
1230         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1231         + "var d = new Date();\n"
1232         + "y = d.getFullYear();\n"
1233         + "m = d.getMonth();\n"
1234         + "d = d.getDate();\n"
1235         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1236         + "if (results && results.length > 0) {";
1237     var regex = "";
1238
1239     var special = false;
1240     var ch = '';
1241     for (var i = 0; i < format.length; ++i) {
1242         ch = format.charAt(i);
1243         if (!special && ch == "\\") {
1244             special = true;
1245         }
1246         else if (special) {
1247             special = false;
1248             regex += String.escape(ch);
1249         }
1250         else {
1251             var obj = Date.formatCodeToRegex(ch, currentGroup);
1252             currentGroup += obj.g;
1253             regex += obj.s;
1254             if (obj.g && obj.c) {
1255                 code += obj.c;
1256             }
1257         }
1258     }
1259
1260     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1261         + "{v = new Date(y, m, d, h, i, s);}\n"
1262         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1265         + "{v = new Date(y, m, d, h);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1267         + "{v = new Date(y, m, d);}\n"
1268         + "else if (y >= 0 && m >= 0)\n"
1269         + "{v = new Date(y, m);}\n"
1270         + "else if (y >= 0)\n"
1271         + "{v = new Date(y);}\n"
1272         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1273         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1274         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1275         + ";}";
1276
1277     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1278     /** eval:var:zzzzzzzzzzzzz */
1279     eval(code);
1280 };
1281
1282 // private
1283 Date.formatCodeToRegex = function(character, currentGroup) {
1284     switch (character) {
1285     case "D":
1286         return {g:0,
1287         c:null,
1288         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1289     case "j":
1290         return {g:1,
1291             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1292             s:"(\\d{1,2})"}; // day of month without leading zeroes
1293     case "d":
1294         return {g:1,
1295             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1296             s:"(\\d{2})"}; // day of month with leading zeroes
1297     case "l":
1298         return {g:0,
1299             c:null,
1300             s:"(?:" + Date.dayNames.join("|") + ")"};
1301     case "S":
1302         return {g:0,
1303             c:null,
1304             s:"(?:st|nd|rd|th)"};
1305     case "w":
1306         return {g:0,
1307             c:null,
1308             s:"\\d"};
1309     case "z":
1310         return {g:0,
1311             c:null,
1312             s:"(?:\\d{1,3})"};
1313     case "W":
1314         return {g:0,
1315             c:null,
1316             s:"(?:\\d{2})"};
1317     case "F":
1318         return {g:1,
1319             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1320             s:"(" + Date.monthNames.join("|") + ")"};
1321     case "M":
1322         return {g:1,
1323             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1324             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1325     case "n":
1326         return {g:1,
1327             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1328             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1329     case "m":
1330         return {g:1,
1331             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1332             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1333     case "t":
1334         return {g:0,
1335             c:null,
1336             s:"\\d{1,2}"};
1337     case "L":
1338         return {g:0,
1339             c:null,
1340             s:"(?:1|0)"};
1341     case "Y":
1342         return {g:1,
1343             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1344             s:"(\\d{4})"};
1345     case "y":
1346         return {g:1,
1347             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1348                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1349             s:"(\\d{1,2})"};
1350     case "a":
1351         return {g:1,
1352             c:"if (results[" + currentGroup + "] == 'am') {\n"
1353                 + "if (h == 12) { h = 0; }\n"
1354                 + "} else { if (h < 12) { h += 12; }}",
1355             s:"(am|pm)"};
1356     case "A":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(AM|PM)"};
1362     case "g":
1363     case "G":
1364         return {g:1,
1365             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1367     case "h":
1368     case "H":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1372     case "i":
1373         return {g:1,
1374             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1375             s:"(\\d{2})"};
1376     case "s":
1377         return {g:1,
1378             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1379             s:"(\\d{2})"};
1380     case "O":
1381         return {g:1,
1382             c:[
1383                 "o = results[", currentGroup, "];\n",
1384                 "var sn = o.substring(0,1);\n", // get + / - sign
1385                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1386                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1387                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1388                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1389             ].join(""),
1390             s:"([+\-]\\d{4})"};
1391     case "P":
1392         return {g:1,
1393                 c:[
1394                    "o = results[", currentGroup, "];\n",
1395                    "var sn = o.substring(0,1);\n",
1396                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1397                    "var mn = o.substring(4,6) % 60;\n",
1398                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1399                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1400             ].join(""),
1401             s:"([+\-]\\d{4})"};
1402     case "T":
1403         return {g:0,
1404             c:null,
1405             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1406     case "Z":
1407         return {g:1,
1408             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1409                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1410             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1411     default:
1412         return {g:0,
1413             c:null,
1414             s:String.escape(character)};
1415     }
1416 };
1417
1418 /**
1419  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1420  * @return {String} The abbreviated timezone name (e.g. 'CST')
1421  */
1422 Date.prototype.getTimezone = function() {
1423     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1424 };
1425
1426 /**
1427  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1428  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1429  */
1430 Date.prototype.getGMTOffset = function() {
1431     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1432         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1433         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1434 };
1435
1436 /**
1437  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1438  * @return {String} 2-characters representing hours and 2-characters representing minutes
1439  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1440  */
1441 Date.prototype.getGMTColonOffset = function() {
1442         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1443                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1444                 + ":"
1445                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1446 }
1447
1448 /**
1449  * Get the numeric day number of the year, adjusted for leap year.
1450  * @return {Number} 0 through 364 (365 in leap years)
1451  */
1452 Date.prototype.getDayOfYear = function() {
1453     var num = 0;
1454     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1455     for (var i = 0; i < this.getMonth(); ++i) {
1456         num += Date.daysInMonth[i];
1457     }
1458     return num + this.getDate() - 1;
1459 };
1460
1461 /**
1462  * Get the string representation of the numeric week number of the year
1463  * (equivalent to the format specifier 'W').
1464  * @return {String} '00' through '52'
1465  */
1466 Date.prototype.getWeekOfYear = function() {
1467     // Skip to Thursday of this week
1468     var now = this.getDayOfYear() + (4 - this.getDay());
1469     // Find the first Thursday of the year
1470     var jan1 = new Date(this.getFullYear(), 0, 1);
1471     var then = (7 - jan1.getDay() + 4);
1472     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1473 };
1474
1475 /**
1476  * Whether or not the current date is in a leap year.
1477  * @return {Boolean} True if the current date is in a leap year, else false
1478  */
1479 Date.prototype.isLeapYear = function() {
1480     var year = this.getFullYear();
1481     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1482 };
1483
1484 /**
1485  * Get the first day of the current month, adjusted for leap year.  The returned value
1486  * is the numeric day index within the week (0-6) which can be used in conjunction with
1487  * the {@link #monthNames} array to retrieve the textual day name.
1488  * Example:
1489  *<pre><code>
1490 var dt = new Date('1/10/2007');
1491 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1492 </code></pre>
1493  * @return {Number} The day number (0-6)
1494  */
1495 Date.prototype.getFirstDayOfMonth = function() {
1496     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1497     return (day < 0) ? (day + 7) : day;
1498 };
1499
1500 /**
1501  * Get the last day of the current month, adjusted for leap year.  The returned value
1502  * is the numeric day index within the week (0-6) which can be used in conjunction with
1503  * the {@link #monthNames} array to retrieve the textual day name.
1504  * Example:
1505  *<pre><code>
1506 var dt = new Date('1/10/2007');
1507 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1508 </code></pre>
1509  * @return {Number} The day number (0-6)
1510  */
1511 Date.prototype.getLastDayOfMonth = function() {
1512     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1513     return (day < 0) ? (day + 7) : day;
1514 };
1515
1516
1517 /**
1518  * Get the first date of this date's month
1519  * @return {Date}
1520  */
1521 Date.prototype.getFirstDateOfMonth = function() {
1522     return new Date(this.getFullYear(), this.getMonth(), 1);
1523 };
1524
1525 /**
1526  * Get the last date of this date's month
1527  * @return {Date}
1528  */
1529 Date.prototype.getLastDateOfMonth = function() {
1530     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1531 };
1532 /**
1533  * Get the number of days in the current month, adjusted for leap year.
1534  * @return {Number} The number of days in the month
1535  */
1536 Date.prototype.getDaysInMonth = function() {
1537     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1538     return Date.daysInMonth[this.getMonth()];
1539 };
1540
1541 /**
1542  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1543  * @return {String} 'st, 'nd', 'rd' or 'th'
1544  */
1545 Date.prototype.getSuffix = function() {
1546     switch (this.getDate()) {
1547         case 1:
1548         case 21:
1549         case 31:
1550             return "st";
1551         case 2:
1552         case 22:
1553             return "nd";
1554         case 3:
1555         case 23:
1556             return "rd";
1557         default:
1558             return "th";
1559     }
1560 };
1561
1562 // private
1563 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1564
1565 /**
1566  * An array of textual month names.
1567  * Override these values for international dates, for example...
1568  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1569  * @type Array
1570  * @static
1571  */
1572 Date.monthNames =
1573    ["January",
1574     "February",
1575     "March",
1576     "April",
1577     "May",
1578     "June",
1579     "July",
1580     "August",
1581     "September",
1582     "October",
1583     "November",
1584     "December"];
1585
1586 /**
1587  * An array of textual day names.
1588  * Override these values for international dates, for example...
1589  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1590  * @type Array
1591  * @static
1592  */
1593 Date.dayNames =
1594    ["Sunday",
1595     "Monday",
1596     "Tuesday",
1597     "Wednesday",
1598     "Thursday",
1599     "Friday",
1600     "Saturday"];
1601
1602 // private
1603 Date.y2kYear = 50;
1604 // private
1605 Date.monthNumbers = {
1606     Jan:0,
1607     Feb:1,
1608     Mar:2,
1609     Apr:3,
1610     May:4,
1611     Jun:5,
1612     Jul:6,
1613     Aug:7,
1614     Sep:8,
1615     Oct:9,
1616     Nov:10,
1617     Dec:11};
1618
1619 /**
1620  * Creates and returns a new Date instance with the exact same date value as the called instance.
1621  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1622  * variable will also be changed.  When the intention is to create a new variable that will not
1623  * modify the original instance, you should create a clone.
1624  *
1625  * Example of correctly cloning a date:
1626  * <pre><code>
1627 //wrong way:
1628 var orig = new Date('10/1/2006');
1629 var copy = orig;
1630 copy.setDate(5);
1631 document.write(orig);  //returns 'Thu Oct 05 2006'!
1632
1633 //correct way:
1634 var orig = new Date('10/1/2006');
1635 var copy = orig.clone();
1636 copy.setDate(5);
1637 document.write(orig);  //returns 'Thu Oct 01 2006'
1638 </code></pre>
1639  * @return {Date} The new Date instance
1640  */
1641 Date.prototype.clone = function() {
1642         return new Date(this.getTime());
1643 };
1644
1645 /**
1646  * Clears any time information from this date
1647  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1648  @return {Date} this or the clone
1649  */
1650 Date.prototype.clearTime = function(clone){
1651     if(clone){
1652         return this.clone().clearTime();
1653     }
1654     this.setHours(0);
1655     this.setMinutes(0);
1656     this.setSeconds(0);
1657     this.setMilliseconds(0);
1658     return this;
1659 };
1660
1661 // private
1662 // safari setMonth is broken
1663 if(Roo.isSafari){
1664     Date.brokenSetMonth = Date.prototype.setMonth;
1665         Date.prototype.setMonth = function(num){
1666                 if(num <= -1){
1667                         var n = Math.ceil(-num);
1668                         var back_year = Math.ceil(n/12);
1669                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1670                         this.setFullYear(this.getFullYear() - back_year);
1671                         return Date.brokenSetMonth.call(this, month);
1672                 } else {
1673                         return Date.brokenSetMonth.apply(this, arguments);
1674                 }
1675         };
1676 }
1677
1678 /** Date interval constant 
1679 * @static 
1680 * @type String */
1681 Date.MILLI = "ms";
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.SECOND = "s";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.MINUTE = "mi";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.HOUR = "h";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.DAY = "d";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.MONTH = "mo";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.YEAR = "y";
1706
1707 /**
1708  * Provides a convenient method of performing basic date arithmetic.  This method
1709  * does not modify the Date instance being called - it creates and returns
1710  * a new Date instance containing the resulting date value.
1711  *
1712  * Examples:
1713  * <pre><code>
1714 //Basic usage:
1715 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1716 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1717
1718 //Negative values will subtract correctly:
1719 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1720 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1721
1722 //You can even chain several calls together in one line!
1723 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1724 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1725  </code></pre>
1726  *
1727  * @param {String} interval   A valid date interval enum value
1728  * @param {Number} value      The amount to add to the current date
1729  * @return {Date} The new Date instance
1730  */
1731 Date.prototype.add = function(interval, value){
1732   var d = this.clone();
1733   if (!interval || value === 0) return d;
1734   switch(interval.toLowerCase()){
1735     case Date.MILLI:
1736       d.setMilliseconds(this.getMilliseconds() + value);
1737       break;
1738     case Date.SECOND:
1739       d.setSeconds(this.getSeconds() + value);
1740       break;
1741     case Date.MINUTE:
1742       d.setMinutes(this.getMinutes() + value);
1743       break;
1744     case Date.HOUR:
1745       d.setHours(this.getHours() + value);
1746       break;
1747     case Date.DAY:
1748       d.setDate(this.getDate() + value);
1749       break;
1750     case Date.MONTH:
1751       var day = this.getDate();
1752       if(day > 28){
1753           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1754       }
1755       d.setDate(day);
1756       d.setMonth(this.getMonth() + value);
1757       break;
1758     case Date.YEAR:
1759       d.setFullYear(this.getFullYear() + value);
1760       break;
1761   }
1762   return d;
1763 };
1764 /*
1765  * Based on:
1766  * Ext JS Library 1.1.1
1767  * Copyright(c) 2006-2007, Ext JS, LLC.
1768  *
1769  * Originally Released Under LGPL - original licence link has changed is not relivant.
1770  *
1771  * Fork - LGPL
1772  * <script type="text/javascript">
1773  */
1774
1775 Roo.lib.Dom = {
1776     getViewWidth : function(full) {
1777         return full ? this.getDocumentWidth() : this.getViewportWidth();
1778     },
1779
1780     getViewHeight : function(full) {
1781         return full ? this.getDocumentHeight() : this.getViewportHeight();
1782     },
1783
1784     getDocumentHeight: function() {
1785         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1786         return Math.max(scrollHeight, this.getViewportHeight());
1787     },
1788
1789     getDocumentWidth: function() {
1790         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1791         return Math.max(scrollWidth, this.getViewportWidth());
1792     },
1793
1794     getViewportHeight: function() {
1795         var height = self.innerHeight;
1796         var mode = document.compatMode;
1797
1798         if ((mode || Roo.isIE) && !Roo.isOpera) {
1799             height = (mode == "CSS1Compat") ?
1800                      document.documentElement.clientHeight :
1801                      document.body.clientHeight;
1802         }
1803
1804         return height;
1805     },
1806
1807     getViewportWidth: function() {
1808         var width = self.innerWidth;
1809         var mode = document.compatMode;
1810
1811         if (mode || Roo.isIE) {
1812             width = (mode == "CSS1Compat") ?
1813                     document.documentElement.clientWidth :
1814                     document.body.clientWidth;
1815         }
1816         return width;
1817     },
1818
1819     isAncestor : function(p, c) {
1820         p = Roo.getDom(p);
1821         c = Roo.getDom(c);
1822         if (!p || !c) {
1823             return false;
1824         }
1825
1826         if (p.contains && !Roo.isSafari) {
1827             return p.contains(c);
1828         } else if (p.compareDocumentPosition) {
1829             return !!(p.compareDocumentPosition(c) & 16);
1830         } else {
1831             var parent = c.parentNode;
1832             while (parent) {
1833                 if (parent == p) {
1834                     return true;
1835                 }
1836                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1837                     return false;
1838                 }
1839                 parent = parent.parentNode;
1840             }
1841             return false;
1842         }
1843     },
1844
1845     getRegion : function(el) {
1846         return Roo.lib.Region.getRegion(el);
1847     },
1848
1849     getY : function(el) {
1850         return this.getXY(el)[1];
1851     },
1852
1853     getX : function(el) {
1854         return this.getXY(el)[0];
1855     },
1856
1857     getXY : function(el) {
1858         var p, pe, b, scroll, bd = document.body;
1859         el = Roo.getDom(el);
1860         var fly = Roo.lib.AnimBase.fly;
1861         if (el.getBoundingClientRect) {
1862             b = el.getBoundingClientRect();
1863             scroll = fly(document).getScroll();
1864             return [b.left + scroll.left, b.top + scroll.top];
1865         }
1866         var x = 0, y = 0;
1867
1868         p = el;
1869
1870         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1871
1872         while (p) {
1873
1874             x += p.offsetLeft;
1875             y += p.offsetTop;
1876
1877             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1878                 hasAbsolute = true;
1879             }
1880
1881             if (Roo.isGecko) {
1882                 pe = fly(p);
1883
1884                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1885                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1886
1887
1888                 x += bl;
1889                 y += bt;
1890
1891
1892                 if (p != el && pe.getStyle('overflow') != 'visible') {
1893                     x += bl;
1894                     y += bt;
1895                 }
1896             }
1897             p = p.offsetParent;
1898         }
1899
1900         if (Roo.isSafari && hasAbsolute) {
1901             x -= bd.offsetLeft;
1902             y -= bd.offsetTop;
1903         }
1904
1905         if (Roo.isGecko && !hasAbsolute) {
1906             var dbd = fly(bd);
1907             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1908             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1909         }
1910
1911         p = el.parentNode;
1912         while (p && p != bd) {
1913             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1914                 x -= p.scrollLeft;
1915                 y -= p.scrollTop;
1916             }
1917             p = p.parentNode;
1918         }
1919         return [x, y];
1920     },
1921  
1922   
1923
1924
1925     setXY : function(el, xy) {
1926         el = Roo.fly(el, '_setXY');
1927         el.position();
1928         var pts = el.translatePoints(xy);
1929         if (xy[0] !== false) {
1930             el.dom.style.left = pts.left + "px";
1931         }
1932         if (xy[1] !== false) {
1933             el.dom.style.top = pts.top + "px";
1934         }
1935     },
1936
1937     setX : function(el, x) {
1938         this.setXY(el, [x, false]);
1939     },
1940
1941     setY : function(el, y) {
1942         this.setXY(el, [false, y]);
1943     }
1944 };
1945 /*
1946  * Portions of this file are based on pieces of Yahoo User Interface Library
1947  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1948  * YUI licensed under the BSD License:
1949  * http://developer.yahoo.net/yui/license.txt
1950  * <script type="text/javascript">
1951  *
1952  */
1953
1954 Roo.lib.Event = function() {
1955     var loadComplete = false;
1956     var listeners = [];
1957     var unloadListeners = [];
1958     var retryCount = 0;
1959     var onAvailStack = [];
1960     var counter = 0;
1961     var lastError = null;
1962
1963     return {
1964         POLL_RETRYS: 200,
1965         POLL_INTERVAL: 20,
1966         EL: 0,
1967         TYPE: 1,
1968         FN: 2,
1969         WFN: 3,
1970         OBJ: 3,
1971         ADJ_SCOPE: 4,
1972         _interval: null,
1973
1974         startInterval: function() {
1975             if (!this._interval) {
1976                 var self = this;
1977                 var callback = function() {
1978                     self._tryPreloadAttach();
1979                 };
1980                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1981
1982             }
1983         },
1984
1985         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1986             onAvailStack.push({ id:         p_id,
1987                 fn:         p_fn,
1988                 obj:        p_obj,
1989                 override:   p_override,
1990                 checkReady: false    });
1991
1992             retryCount = this.POLL_RETRYS;
1993             this.startInterval();
1994         },
1995
1996
1997         addListener: function(el, eventName, fn) {
1998             el = Roo.getDom(el);
1999             if (!el || !fn) {
2000                 return false;
2001             }
2002
2003             if ("unload" == eventName) {
2004                 unloadListeners[unloadListeners.length] =
2005                 [el, eventName, fn];
2006                 return true;
2007             }
2008
2009             var wrappedFn = function(e) {
2010                 return fn(Roo.lib.Event.getEvent(e));
2011             };
2012
2013             var li = [el, eventName, fn, wrappedFn];
2014
2015             var index = listeners.length;
2016             listeners[index] = li;
2017
2018             this.doAdd(el, eventName, wrappedFn, false);
2019             return true;
2020
2021         },
2022
2023
2024         removeListener: function(el, eventName, fn) {
2025             var i, len;
2026
2027             el = Roo.getDom(el);
2028
2029             if(!fn) {
2030                 return this.purgeElement(el, false, eventName);
2031             }
2032
2033
2034             if ("unload" == eventName) {
2035
2036                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2037                     var li = unloadListeners[i];
2038                     if (li &&
2039                         li[0] == el &&
2040                         li[1] == eventName &&
2041                         li[2] == fn) {
2042                         unloadListeners.splice(i, 1);
2043                         return true;
2044                     }
2045                 }
2046
2047                 return false;
2048             }
2049
2050             var cacheItem = null;
2051
2052
2053             var index = arguments[3];
2054
2055             if ("undefined" == typeof index) {
2056                 index = this._getCacheIndex(el, eventName, fn);
2057             }
2058
2059             if (index >= 0) {
2060                 cacheItem = listeners[index];
2061             }
2062
2063             if (!el || !cacheItem) {
2064                 return false;
2065             }
2066
2067             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2068
2069             delete listeners[index][this.WFN];
2070             delete listeners[index][this.FN];
2071             listeners.splice(index, 1);
2072
2073             return true;
2074
2075         },
2076
2077
2078         getTarget: function(ev, resolveTextNode) {
2079             ev = ev.browserEvent || ev;
2080             var t = ev.target || ev.srcElement;
2081             return this.resolveTextNode(t);
2082         },
2083
2084
2085         resolveTextNode: function(node) {
2086             if (Roo.isSafari && node && 3 == node.nodeType) {
2087                 return node.parentNode;
2088             } else {
2089                 return node;
2090             }
2091         },
2092
2093
2094         getPageX: function(ev) {
2095             ev = ev.browserEvent || ev;
2096             var x = ev.pageX;
2097             if (!x && 0 !== x) {
2098                 x = ev.clientX || 0;
2099
2100                 if (Roo.isIE) {
2101                     x += this.getScroll()[1];
2102                 }
2103             }
2104
2105             return x;
2106         },
2107
2108
2109         getPageY: function(ev) {
2110             ev = ev.browserEvent || ev;
2111             var y = ev.pageY;
2112             if (!y && 0 !== y) {
2113                 y = ev.clientY || 0;
2114
2115                 if (Roo.isIE) {
2116                     y += this.getScroll()[0];
2117                 }
2118             }
2119
2120
2121             return y;
2122         },
2123
2124
2125         getXY: function(ev) {
2126             ev = ev.browserEvent || ev;
2127             return [this.getPageX(ev), this.getPageY(ev)];
2128         },
2129
2130
2131         getRelatedTarget: function(ev) {
2132             ev = ev.browserEvent || ev;
2133             var t = ev.relatedTarget;
2134             if (!t) {
2135                 if (ev.type == "mouseout") {
2136                     t = ev.toElement;
2137                 } else if (ev.type == "mouseover") {
2138                     t = ev.fromElement;
2139                 }
2140             }
2141
2142             return this.resolveTextNode(t);
2143         },
2144
2145
2146         getTime: function(ev) {
2147             ev = ev.browserEvent || ev;
2148             if (!ev.time) {
2149                 var t = new Date().getTime();
2150                 try {
2151                     ev.time = t;
2152                 } catch(ex) {
2153                     this.lastError = ex;
2154                     return t;
2155                 }
2156             }
2157
2158             return ev.time;
2159         },
2160
2161
2162         stopEvent: function(ev) {
2163             this.stopPropagation(ev);
2164             this.preventDefault(ev);
2165         },
2166
2167
2168         stopPropagation: function(ev) {
2169             ev = ev.browserEvent || ev;
2170             if (ev.stopPropagation) {
2171                 ev.stopPropagation();
2172             } else {
2173                 ev.cancelBubble = true;
2174             }
2175         },
2176
2177
2178         preventDefault: function(ev) {
2179             ev = ev.browserEvent || ev;
2180             if(ev.preventDefault) {
2181                 ev.preventDefault();
2182             } else {
2183                 ev.returnValue = false;
2184             }
2185         },
2186
2187
2188         getEvent: function(e) {
2189             var ev = e || window.event;
2190             if (!ev) {
2191                 var c = this.getEvent.caller;
2192                 while (c) {
2193                     ev = c.arguments[0];
2194                     if (ev && Event == ev.constructor) {
2195                         break;
2196                     }
2197                     c = c.caller;
2198                 }
2199             }
2200             return ev;
2201         },
2202
2203
2204         getCharCode: function(ev) {
2205             ev = ev.browserEvent || ev;
2206             return ev.charCode || ev.keyCode || 0;
2207         },
2208
2209
2210         _getCacheIndex: function(el, eventName, fn) {
2211             for (var i = 0,len = listeners.length; i < len; ++i) {
2212                 var li = listeners[i];
2213                 if (li &&
2214                     li[this.FN] == fn &&
2215                     li[this.EL] == el &&
2216                     li[this.TYPE] == eventName) {
2217                     return i;
2218                 }
2219             }
2220
2221             return -1;
2222         },
2223
2224
2225         elCache: {},
2226
2227
2228         getEl: function(id) {
2229             return document.getElementById(id);
2230         },
2231
2232
2233         clearCache: function() {
2234         },
2235
2236
2237         _load: function(e) {
2238             loadComplete = true;
2239             var EU = Roo.lib.Event;
2240
2241
2242             if (Roo.isIE) {
2243                 EU.doRemove(window, "load", EU._load);
2244             }
2245         },
2246
2247
2248         _tryPreloadAttach: function() {
2249
2250             if (this.locked) {
2251                 return false;
2252             }
2253
2254             this.locked = true;
2255
2256
2257             var tryAgain = !loadComplete;
2258             if (!tryAgain) {
2259                 tryAgain = (retryCount > 0);
2260             }
2261
2262
2263             var notAvail = [];
2264             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2265                 var item = onAvailStack[i];
2266                 if (item) {
2267                     var el = this.getEl(item.id);
2268
2269                     if (el) {
2270                         if (!item.checkReady ||
2271                             loadComplete ||
2272                             el.nextSibling ||
2273                             (document && document.body)) {
2274
2275                             var scope = el;
2276                             if (item.override) {
2277                                 if (item.override === true) {
2278                                     scope = item.obj;
2279                                 } else {
2280                                     scope = item.override;
2281                                 }
2282                             }
2283                             item.fn.call(scope, item.obj);
2284                             onAvailStack[i] = null;
2285                         }
2286                     } else {
2287                         notAvail.push(item);
2288                     }
2289                 }
2290             }
2291
2292             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2293
2294             if (tryAgain) {
2295
2296                 this.startInterval();
2297             } else {
2298                 clearInterval(this._interval);
2299                 this._interval = null;
2300             }
2301
2302             this.locked = false;
2303
2304             return true;
2305
2306         },
2307
2308
2309         purgeElement: function(el, recurse, eventName) {
2310             var elListeners = this.getListeners(el, eventName);
2311             if (elListeners) {
2312                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2313                     var l = elListeners[i];
2314                     this.removeListener(el, l.type, l.fn);
2315                 }
2316             }
2317
2318             if (recurse && el && el.childNodes) {
2319                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2320                     this.purgeElement(el.childNodes[i], recurse, eventName);
2321                 }
2322             }
2323         },
2324
2325
2326         getListeners: function(el, eventName) {
2327             var results = [], searchLists;
2328             if (!eventName) {
2329                 searchLists = [listeners, unloadListeners];
2330             } else if (eventName == "unload") {
2331                 searchLists = [unloadListeners];
2332             } else {
2333                 searchLists = [listeners];
2334             }
2335
2336             for (var j = 0; j < searchLists.length; ++j) {
2337                 var searchList = searchLists[j];
2338                 if (searchList && searchList.length > 0) {
2339                     for (var i = 0,len = searchList.length; i < len; ++i) {
2340                         var l = searchList[i];
2341                         if (l && l[this.EL] === el &&
2342                             (!eventName || eventName === l[this.TYPE])) {
2343                             results.push({
2344                                 type:   l[this.TYPE],
2345                                 fn:     l[this.FN],
2346                                 obj:    l[this.OBJ],
2347                                 adjust: l[this.ADJ_SCOPE],
2348                                 index:  i
2349                             });
2350                         }
2351                     }
2352                 }
2353             }
2354
2355             return (results.length) ? results : null;
2356         },
2357
2358
2359         _unload: function(e) {
2360
2361             var EU = Roo.lib.Event, i, j, l, len, index;
2362
2363             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2364                 l = unloadListeners[i];
2365                 if (l) {
2366                     var scope = window;
2367                     if (l[EU.ADJ_SCOPE]) {
2368                         if (l[EU.ADJ_SCOPE] === true) {
2369                             scope = l[EU.OBJ];
2370                         } else {
2371                             scope = l[EU.ADJ_SCOPE];
2372                         }
2373                     }
2374                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2375                     unloadListeners[i] = null;
2376                     l = null;
2377                     scope = null;
2378                 }
2379             }
2380
2381             unloadListeners = null;
2382
2383             if (listeners && listeners.length > 0) {
2384                 j = listeners.length;
2385                 while (j) {
2386                     index = j - 1;
2387                     l = listeners[index];
2388                     if (l) {
2389                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2390                                 l[EU.FN], index);
2391                     }
2392                     j = j - 1;
2393                 }
2394                 l = null;
2395
2396                 EU.clearCache();
2397             }
2398
2399             EU.doRemove(window, "unload", EU._unload);
2400
2401         },
2402
2403
2404         getScroll: function() {
2405             var dd = document.documentElement, db = document.body;
2406             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2407                 return [dd.scrollTop, dd.scrollLeft];
2408             } else if (db) {
2409                 return [db.scrollTop, db.scrollLeft];
2410             } else {
2411                 return [0, 0];
2412             }
2413         },
2414
2415
2416         doAdd: function () {
2417             if (window.addEventListener) {
2418                 return function(el, eventName, fn, capture) {
2419                     el.addEventListener(eventName, fn, (capture));
2420                 };
2421             } else if (window.attachEvent) {
2422                 return function(el, eventName, fn, capture) {
2423                     el.attachEvent("on" + eventName, fn);
2424                 };
2425             } else {
2426                 return function() {
2427                 };
2428             }
2429         }(),
2430
2431
2432         doRemove: function() {
2433             if (window.removeEventListener) {
2434                 return function (el, eventName, fn, capture) {
2435                     el.removeEventListener(eventName, fn, (capture));
2436                 };
2437             } else if (window.detachEvent) {
2438                 return function (el, eventName, fn) {
2439                     el.detachEvent("on" + eventName, fn);
2440                 };
2441             } else {
2442                 return function() {
2443                 };
2444             }
2445         }()
2446     };
2447     
2448 }();
2449 (function() {     
2450    
2451     var E = Roo.lib.Event;
2452     E.on = E.addListener;
2453     E.un = E.removeListener;
2454
2455     if (document && document.body) {
2456         E._load();
2457     } else {
2458         E.doAdd(window, "load", E._load);
2459     }
2460     E.doAdd(window, "unload", E._unload);
2461     E._tryPreloadAttach();
2462 })();
2463
2464 /*
2465  * Portions of this file are based on pieces of Yahoo User Interface Library
2466  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2467  * YUI licensed under the BSD License:
2468  * http://developer.yahoo.net/yui/license.txt
2469  * <script type="text/javascript">
2470  *
2471  */
2472
2473 (function() {
2474     /**
2475      * @class Roo.lib.Ajax
2476      *
2477      */
2478     Roo.lib.Ajax = {
2479         /**
2480          * @static 
2481          */
2482         request : function(method, uri, cb, data, options) {
2483             if(options){
2484                 var hs = options.headers;
2485                 if(hs){
2486                     for(var h in hs){
2487                         if(hs.hasOwnProperty(h)){
2488                             this.initHeader(h, hs[h], false);
2489                         }
2490                     }
2491                 }
2492                 if(options.xmlData){
2493                     this.initHeader('Content-Type', 'text/xml', false);
2494                     method = 'POST';
2495                     data = options.xmlData;
2496                 }
2497             }
2498
2499             return this.asyncRequest(method, uri, cb, data);
2500         },
2501
2502         serializeForm : function(form) {
2503             if(typeof form == 'string') {
2504                 form = (document.getElementById(form) || document.forms[form]);
2505             }
2506
2507             var el, name, val, disabled, data = '', hasSubmit = false;
2508             for (var i = 0; i < form.elements.length; i++) {
2509                 el = form.elements[i];
2510                 disabled = form.elements[i].disabled;
2511                 name = form.elements[i].name;
2512                 val = form.elements[i].value;
2513
2514                 if (!disabled && name){
2515                     switch (el.type)
2516                             {
2517                         case 'select-one':
2518                         case 'select-multiple':
2519                             for (var j = 0; j < el.options.length; j++) {
2520                                 if (el.options[j].selected) {
2521                                     if (Roo.isIE) {
2522                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2523                                     }
2524                                     else {
2525                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2526                                     }
2527                                 }
2528                             }
2529                             break;
2530                         case 'radio':
2531                         case 'checkbox':
2532                             if (el.checked) {
2533                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2534                             }
2535                             break;
2536                         case 'file':
2537
2538                         case undefined:
2539
2540                         case 'reset':
2541
2542                         case 'button':
2543
2544                             break;
2545                         case 'submit':
2546                             if(hasSubmit == false) {
2547                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2548                                 hasSubmit = true;
2549                             }
2550                             break;
2551                         default:
2552                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2553                             break;
2554                     }
2555                 }
2556             }
2557             data = data.substr(0, data.length - 1);
2558             return data;
2559         },
2560
2561         headers:{},
2562
2563         hasHeaders:false,
2564
2565         useDefaultHeader:true,
2566
2567         defaultPostHeader:'application/x-www-form-urlencoded',
2568
2569         useDefaultXhrHeader:true,
2570
2571         defaultXhrHeader:'XMLHttpRequest',
2572
2573         hasDefaultHeaders:true,
2574
2575         defaultHeaders:{},
2576
2577         poll:{},
2578
2579         timeout:{},
2580
2581         pollInterval:50,
2582
2583         transactionId:0,
2584
2585         setProgId:function(id)
2586         {
2587             this.activeX.unshift(id);
2588         },
2589
2590         setDefaultPostHeader:function(b)
2591         {
2592             this.useDefaultHeader = b;
2593         },
2594
2595         setDefaultXhrHeader:function(b)
2596         {
2597             this.useDefaultXhrHeader = b;
2598         },
2599
2600         setPollingInterval:function(i)
2601         {
2602             if (typeof i == 'number' && isFinite(i)) {
2603                 this.pollInterval = i;
2604             }
2605         },
2606
2607         createXhrObject:function(transactionId)
2608         {
2609             var obj,http;
2610             try
2611             {
2612
2613                 http = new XMLHttpRequest();
2614
2615                 obj = { conn:http, tId:transactionId };
2616             }
2617             catch(e)
2618             {
2619                 for (var i = 0; i < this.activeX.length; ++i) {
2620                     try
2621                     {
2622
2623                         http = new ActiveXObject(this.activeX[i]);
2624
2625                         obj = { conn:http, tId:transactionId };
2626                         break;
2627                     }
2628                     catch(e) {
2629                     }
2630                 }
2631             }
2632             finally
2633             {
2634                 return obj;
2635             }
2636         },
2637
2638         getConnectionObject:function()
2639         {
2640             var o;
2641             var tId = this.transactionId;
2642
2643             try
2644             {
2645                 o = this.createXhrObject(tId);
2646                 if (o) {
2647                     this.transactionId++;
2648                 }
2649             }
2650             catch(e) {
2651             }
2652             finally
2653             {
2654                 return o;
2655             }
2656         },
2657
2658         asyncRequest:function(method, uri, callback, postData)
2659         {
2660             var o = this.getConnectionObject();
2661
2662             if (!o) {
2663                 return null;
2664             }
2665             else {
2666                 o.conn.open(method, uri, true);
2667
2668                 if (this.useDefaultXhrHeader) {
2669                     if (!this.defaultHeaders['X-Requested-With']) {
2670                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2671                     }
2672                 }
2673
2674                 if(postData && this.useDefaultHeader){
2675                     this.initHeader('Content-Type', this.defaultPostHeader);
2676                 }
2677
2678                  if (this.hasDefaultHeaders || this.hasHeaders) {
2679                     this.setHeader(o);
2680                 }
2681
2682                 this.handleReadyState(o, callback);
2683                 o.conn.send(postData || null);
2684
2685                 return o;
2686             }
2687         },
2688
2689         handleReadyState:function(o, callback)
2690         {
2691             var oConn = this;
2692
2693             if (callback && callback.timeout) {
2694                 this.timeout[o.tId] = window.setTimeout(function() {
2695                     oConn.abort(o, callback, true);
2696                 }, callback.timeout);
2697             }
2698
2699             this.poll[o.tId] = window.setInterval(
2700                     function() {
2701                         if (o.conn && o.conn.readyState == 4) {
2702                             window.clearInterval(oConn.poll[o.tId]);
2703                             delete oConn.poll[o.tId];
2704
2705                             if(callback && callback.timeout) {
2706                                 window.clearTimeout(oConn.timeout[o.tId]);
2707                                 delete oConn.timeout[o.tId];
2708                             }
2709
2710                             oConn.handleTransactionResponse(o, callback);
2711                         }
2712                     }
2713                     , this.pollInterval);
2714         },
2715
2716         handleTransactionResponse:function(o, callback, isAbort)
2717         {
2718
2719             if (!callback) {
2720                 this.releaseObject(o);
2721                 return;
2722             }
2723
2724             var httpStatus, responseObject;
2725
2726             try
2727             {
2728                 if (o.conn.status !== undefined && o.conn.status != 0) {
2729                     httpStatus = o.conn.status;
2730                 }
2731                 else {
2732                     httpStatus = 13030;
2733                 }
2734             }
2735             catch(e) {
2736
2737
2738                 httpStatus = 13030;
2739             }
2740
2741             if (httpStatus >= 200 && httpStatus < 300) {
2742                 responseObject = this.createResponseObject(o, callback.argument);
2743                 if (callback.success) {
2744                     if (!callback.scope) {
2745                         callback.success(responseObject);
2746                     }
2747                     else {
2748
2749
2750                         callback.success.apply(callback.scope, [responseObject]);
2751                     }
2752                 }
2753             }
2754             else {
2755                 switch (httpStatus) {
2756
2757                     case 12002:
2758                     case 12029:
2759                     case 12030:
2760                     case 12031:
2761                     case 12152:
2762                     case 13030:
2763                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2764                         if (callback.failure) {
2765                             if (!callback.scope) {
2766                                 callback.failure(responseObject);
2767                             }
2768                             else {
2769                                 callback.failure.apply(callback.scope, [responseObject]);
2770                             }
2771                         }
2772                         break;
2773                     default:
2774                         responseObject = this.createResponseObject(o, callback.argument);
2775                         if (callback.failure) {
2776                             if (!callback.scope) {
2777                                 callback.failure(responseObject);
2778                             }
2779                             else {
2780                                 callback.failure.apply(callback.scope, [responseObject]);
2781                             }
2782                         }
2783                 }
2784             }
2785
2786             this.releaseObject(o);
2787             responseObject = null;
2788         },
2789
2790         createResponseObject:function(o, callbackArg)
2791         {
2792             var obj = {};
2793             var headerObj = {};
2794
2795             try
2796             {
2797                 var headerStr = o.conn.getAllResponseHeaders();
2798                 var header = headerStr.split('\n');
2799                 for (var i = 0; i < header.length; i++) {
2800                     var delimitPos = header[i].indexOf(':');
2801                     if (delimitPos != -1) {
2802                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2803                     }
2804                 }
2805             }
2806             catch(e) {
2807             }
2808
2809             obj.tId = o.tId;
2810             obj.status = o.conn.status;
2811             obj.statusText = o.conn.statusText;
2812             obj.getResponseHeader = headerObj;
2813             obj.getAllResponseHeaders = headerStr;
2814             obj.responseText = o.conn.responseText;
2815             obj.responseXML = o.conn.responseXML;
2816
2817             if (typeof callbackArg !== undefined) {
2818                 obj.argument = callbackArg;
2819             }
2820
2821             return obj;
2822         },
2823
2824         createExceptionObject:function(tId, callbackArg, isAbort)
2825         {
2826             var COMM_CODE = 0;
2827             var COMM_ERROR = 'communication failure';
2828             var ABORT_CODE = -1;
2829             var ABORT_ERROR = 'transaction aborted';
2830
2831             var obj = {};
2832
2833             obj.tId = tId;
2834             if (isAbort) {
2835                 obj.status = ABORT_CODE;
2836                 obj.statusText = ABORT_ERROR;
2837             }
2838             else {
2839                 obj.status = COMM_CODE;
2840                 obj.statusText = COMM_ERROR;
2841             }
2842
2843             if (callbackArg) {
2844                 obj.argument = callbackArg;
2845             }
2846
2847             return obj;
2848         },
2849
2850         initHeader:function(label, value, isDefault)
2851         {
2852             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2853
2854             if (headerObj[label] === undefined) {
2855                 headerObj[label] = value;
2856             }
2857             else {
2858
2859
2860                 headerObj[label] = value + "," + headerObj[label];
2861             }
2862
2863             if (isDefault) {
2864                 this.hasDefaultHeaders = true;
2865             }
2866             else {
2867                 this.hasHeaders = true;
2868             }
2869         },
2870
2871
2872         setHeader:function(o)
2873         {
2874             if (this.hasDefaultHeaders) {
2875                 for (var prop in this.defaultHeaders) {
2876                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2877                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2878                     }
2879                 }
2880             }
2881
2882             if (this.hasHeaders) {
2883                 for (var prop in this.headers) {
2884                     if (this.headers.hasOwnProperty(prop)) {
2885                         o.conn.setRequestHeader(prop, this.headers[prop]);
2886                     }
2887                 }
2888                 this.headers = {};
2889                 this.hasHeaders = false;
2890             }
2891         },
2892
2893         resetDefaultHeaders:function() {
2894             delete this.defaultHeaders;
2895             this.defaultHeaders = {};
2896             this.hasDefaultHeaders = false;
2897         },
2898
2899         abort:function(o, callback, isTimeout)
2900         {
2901             if(this.isCallInProgress(o)) {
2902                 o.conn.abort();
2903                 window.clearInterval(this.poll[o.tId]);
2904                 delete this.poll[o.tId];
2905                 if (isTimeout) {
2906                     delete this.timeout[o.tId];
2907                 }
2908
2909                 this.handleTransactionResponse(o, callback, true);
2910
2911                 return true;
2912             }
2913             else {
2914                 return false;
2915             }
2916         },
2917
2918
2919         isCallInProgress:function(o)
2920         {
2921             if (o && o.conn) {
2922                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2923             }
2924             else {
2925
2926                 return false;
2927             }
2928         },
2929
2930
2931         releaseObject:function(o)
2932         {
2933
2934             o.conn = null;
2935
2936             o = null;
2937         },
2938
2939         activeX:[
2940         'MSXML2.XMLHTTP.3.0',
2941         'MSXML2.XMLHTTP',
2942         'Microsoft.XMLHTTP'
2943         ]
2944
2945
2946     };
2947 })();/*
2948  * Portions of this file are based on pieces of Yahoo User Interface Library
2949  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2950  * YUI licensed under the BSD License:
2951  * http://developer.yahoo.net/yui/license.txt
2952  * <script type="text/javascript">
2953  *
2954  */
2955
2956 Roo.lib.Region = function(t, r, b, l) {
2957     this.top = t;
2958     this[1] = t;
2959     this.right = r;
2960     this.bottom = b;
2961     this.left = l;
2962     this[0] = l;
2963 };
2964
2965
2966 Roo.lib.Region.prototype = {
2967     contains : function(region) {
2968         return ( region.left >= this.left &&
2969                  region.right <= this.right &&
2970                  region.top >= this.top &&
2971                  region.bottom <= this.bottom    );
2972
2973     },
2974
2975     getArea : function() {
2976         return ( (this.bottom - this.top) * (this.right - this.left) );
2977     },
2978
2979     intersect : function(region) {
2980         var t = Math.max(this.top, region.top);
2981         var r = Math.min(this.right, region.right);
2982         var b = Math.min(this.bottom, region.bottom);
2983         var l = Math.max(this.left, region.left);
2984
2985         if (b >= t && r >= l) {
2986             return new Roo.lib.Region(t, r, b, l);
2987         } else {
2988             return null;
2989         }
2990     },
2991     union : function(region) {
2992         var t = Math.min(this.top, region.top);
2993         var r = Math.max(this.right, region.right);
2994         var b = Math.max(this.bottom, region.bottom);
2995         var l = Math.min(this.left, region.left);
2996
2997         return new Roo.lib.Region(t, r, b, l);
2998     },
2999
3000     adjust : function(t, l, b, r) {
3001         this.top += t;
3002         this.left += l;
3003         this.right += r;
3004         this.bottom += b;
3005         return this;
3006     }
3007 };
3008
3009 Roo.lib.Region.getRegion = function(el) {
3010     var p = Roo.lib.Dom.getXY(el);
3011
3012     var t = p[1];
3013     var r = p[0] + el.offsetWidth;
3014     var b = p[1] + el.offsetHeight;
3015     var l = p[0];
3016
3017     return new Roo.lib.Region(t, r, b, l);
3018 };
3019 /*
3020  * Portions of this file are based on pieces of Yahoo User Interface Library
3021  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3022  * YUI licensed under the BSD License:
3023  * http://developer.yahoo.net/yui/license.txt
3024  * <script type="text/javascript">
3025  *
3026  */
3027 //@@dep Roo.lib.Region
3028
3029
3030 Roo.lib.Point = function(x, y) {
3031     if (x instanceof Array) {
3032         y = x[1];
3033         x = x[0];
3034     }
3035     this.x = this.right = this.left = this[0] = x;
3036     this.y = this.top = this.bottom = this[1] = y;
3037 };
3038
3039 Roo.lib.Point.prototype = new Roo.lib.Region();
3040 /*
3041  * Portions of this file are based on pieces of Yahoo User Interface Library
3042  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3043  * YUI licensed under the BSD License:
3044  * http://developer.yahoo.net/yui/license.txt
3045  * <script type="text/javascript">
3046  *
3047  */
3048  
3049 (function() {   
3050
3051     Roo.lib.Anim = {
3052         scroll : function(el, args, duration, easing, cb, scope) {
3053             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3054         },
3055
3056         motion : function(el, args, duration, easing, cb, scope) {
3057             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3058         },
3059
3060         color : function(el, args, duration, easing, cb, scope) {
3061             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3062         },
3063
3064         run : function(el, args, duration, easing, cb, scope, type) {
3065             type = type || Roo.lib.AnimBase;
3066             if (typeof easing == "string") {
3067                 easing = Roo.lib.Easing[easing];
3068             }
3069             var anim = new type(el, args, duration, easing);
3070             anim.animateX(function() {
3071                 Roo.callback(cb, scope);
3072             });
3073             return anim;
3074         }
3075     };
3076 })();/*
3077  * Portions of this file are based on pieces of Yahoo User Interface Library
3078  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3079  * YUI licensed under the BSD License:
3080  * http://developer.yahoo.net/yui/license.txt
3081  * <script type="text/javascript">
3082  *
3083  */
3084
3085 (function() {    
3086     var libFlyweight;
3087     
3088     function fly(el) {
3089         if (!libFlyweight) {
3090             libFlyweight = new Roo.Element.Flyweight();
3091         }
3092         libFlyweight.dom = el;
3093         return libFlyweight;
3094     }
3095
3096     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3097     
3098    
3099     
3100     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3101         if (el) {
3102             this.init(el, attributes, duration, method);
3103         }
3104     };
3105
3106     Roo.lib.AnimBase.fly = fly;
3107     
3108     
3109     
3110     Roo.lib.AnimBase.prototype = {
3111
3112         toString: function() {
3113             var el = this.getEl();
3114             var id = el.id || el.tagName;
3115             return ("Anim " + id);
3116         },
3117
3118         patterns: {
3119             noNegatives:        /width|height|opacity|padding/i,
3120             offsetAttribute:  /^((width|height)|(top|left))$/,
3121             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3122             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3123         },
3124
3125
3126         doMethod: function(attr, start, end) {
3127             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3128         },
3129
3130
3131         setAttribute: function(attr, val, unit) {
3132             if (this.patterns.noNegatives.test(attr)) {
3133                 val = (val > 0) ? val : 0;
3134             }
3135
3136             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3137         },
3138
3139
3140         getAttribute: function(attr) {
3141             var el = this.getEl();
3142             var val = fly(el).getStyle(attr);
3143
3144             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3145                 return parseFloat(val);
3146             }
3147
3148             var a = this.patterns.offsetAttribute.exec(attr) || [];
3149             var pos = !!( a[3] );
3150             var box = !!( a[2] );
3151
3152
3153             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3154                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3155             } else {
3156                 val = 0;
3157             }
3158
3159             return val;
3160         },
3161
3162
3163         getDefaultUnit: function(attr) {
3164             if (this.patterns.defaultUnit.test(attr)) {
3165                 return 'px';
3166             }
3167
3168             return '';
3169         },
3170
3171         animateX : function(callback, scope) {
3172             var f = function() {
3173                 this.onComplete.removeListener(f);
3174                 if (typeof callback == "function") {
3175                     callback.call(scope || this, this);
3176                 }
3177             };
3178             this.onComplete.addListener(f, this);
3179             this.animate();
3180         },
3181
3182
3183         setRuntimeAttribute: function(attr) {
3184             var start;
3185             var end;
3186             var attributes = this.attributes;
3187
3188             this.runtimeAttributes[attr] = {};
3189
3190             var isset = function(prop) {
3191                 return (typeof prop !== 'undefined');
3192             };
3193
3194             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3195                 return false;
3196             }
3197
3198             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3199
3200
3201             if (isset(attributes[attr]['to'])) {
3202                 end = attributes[attr]['to'];
3203             } else if (isset(attributes[attr]['by'])) {
3204                 if (start.constructor == Array) {
3205                     end = [];
3206                     for (var i = 0, len = start.length; i < len; ++i) {
3207                         end[i] = start[i] + attributes[attr]['by'][i];
3208                     }
3209                 } else {
3210                     end = start + attributes[attr]['by'];
3211                 }
3212             }
3213
3214             this.runtimeAttributes[attr].start = start;
3215             this.runtimeAttributes[attr].end = end;
3216
3217
3218             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3219         },
3220
3221
3222         init: function(el, attributes, duration, method) {
3223
3224             var isAnimated = false;
3225
3226
3227             var startTime = null;
3228
3229
3230             var actualFrames = 0;
3231
3232
3233             el = Roo.getDom(el);
3234
3235
3236             this.attributes = attributes || {};
3237
3238
3239             this.duration = duration || 1;
3240
3241
3242             this.method = method || Roo.lib.Easing.easeNone;
3243
3244
3245             this.useSeconds = true;
3246
3247
3248             this.currentFrame = 0;
3249
3250
3251             this.totalFrames = Roo.lib.AnimMgr.fps;
3252
3253
3254             this.getEl = function() {
3255                 return el;
3256             };
3257
3258
3259             this.isAnimated = function() {
3260                 return isAnimated;
3261             };
3262
3263
3264             this.getStartTime = function() {
3265                 return startTime;
3266             };
3267
3268             this.runtimeAttributes = {};
3269
3270
3271             this.animate = function() {
3272                 if (this.isAnimated()) {
3273                     return false;
3274                 }
3275
3276                 this.currentFrame = 0;
3277
3278                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3279
3280                 Roo.lib.AnimMgr.registerElement(this);
3281             };
3282
3283
3284             this.stop = function(finish) {
3285                 if (finish) {
3286                     this.currentFrame = this.totalFrames;
3287                     this._onTween.fire();
3288                 }
3289                 Roo.lib.AnimMgr.stop(this);
3290             };
3291
3292             var onStart = function() {
3293                 this.onStart.fire();
3294
3295                 this.runtimeAttributes = {};
3296                 for (var attr in this.attributes) {
3297                     this.setRuntimeAttribute(attr);
3298                 }
3299
3300                 isAnimated = true;
3301                 actualFrames = 0;
3302                 startTime = new Date();
3303             };
3304
3305
3306             var onTween = function() {
3307                 var data = {
3308                     duration: new Date() - this.getStartTime(),
3309                     currentFrame: this.currentFrame
3310                 };
3311
3312                 data.toString = function() {
3313                     return (
3314                             'duration: ' + data.duration +
3315                             ', currentFrame: ' + data.currentFrame
3316                             );
3317                 };
3318
3319                 this.onTween.fire(data);
3320
3321                 var runtimeAttributes = this.runtimeAttributes;
3322
3323                 for (var attr in runtimeAttributes) {
3324                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3325                 }
3326
3327                 actualFrames += 1;
3328             };
3329
3330             var onComplete = function() {
3331                 var actual_duration = (new Date() - startTime) / 1000 ;
3332
3333                 var data = {
3334                     duration: actual_duration,
3335                     frames: actualFrames,
3336                     fps: actualFrames / actual_duration
3337                 };
3338
3339                 data.toString = function() {
3340                     return (
3341                             'duration: ' + data.duration +
3342                             ', frames: ' + data.frames +
3343                             ', fps: ' + data.fps
3344                             );
3345                 };
3346
3347                 isAnimated = false;
3348                 actualFrames = 0;
3349                 this.onComplete.fire(data);
3350             };
3351
3352
3353             this._onStart = new Roo.util.Event(this);
3354             this.onStart = new Roo.util.Event(this);
3355             this.onTween = new Roo.util.Event(this);
3356             this._onTween = new Roo.util.Event(this);
3357             this.onComplete = new Roo.util.Event(this);
3358             this._onComplete = new Roo.util.Event(this);
3359             this._onStart.addListener(onStart);
3360             this._onTween.addListener(onTween);
3361             this._onComplete.addListener(onComplete);
3362         }
3363     };
3364 })();
3365 /*
3366  * Portions of this file are based on pieces of Yahoo User Interface Library
3367  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3368  * YUI licensed under the BSD License:
3369  * http://developer.yahoo.net/yui/license.txt
3370  * <script type="text/javascript">
3371  *
3372  */
3373
3374 Roo.lib.AnimMgr = new function() {
3375
3376         var thread = null;
3377
3378
3379         var queue = [];
3380
3381
3382         var tweenCount = 0;
3383
3384
3385         this.fps = 1000;
3386
3387
3388         this.delay = 1;
3389
3390
3391         this.registerElement = function(tween) {
3392             queue[queue.length] = tween;
3393             tweenCount += 1;
3394             tween._onStart.fire();
3395             this.start();
3396         };
3397
3398
3399         this.unRegister = function(tween, index) {
3400             tween._onComplete.fire();
3401             index = index || getIndex(tween);
3402             if (index != -1) {
3403                 queue.splice(index, 1);
3404             }
3405
3406             tweenCount -= 1;
3407             if (tweenCount <= 0) {
3408                 this.stop();
3409             }
3410         };
3411
3412
3413         this.start = function() {
3414             if (thread === null) {
3415                 thread = setInterval(this.run, this.delay);
3416             }
3417         };
3418
3419
3420         this.stop = function(tween) {
3421             if (!tween) {
3422                 clearInterval(thread);
3423
3424                 for (var i = 0, len = queue.length; i < len; ++i) {
3425                     if (queue[0].isAnimated()) {
3426                         this.unRegister(queue[0], 0);
3427                     }
3428                 }
3429
3430                 queue = [];
3431                 thread = null;
3432                 tweenCount = 0;
3433             }
3434             else {
3435                 this.unRegister(tween);
3436             }
3437         };
3438
3439
3440         this.run = function() {
3441             for (var i = 0, len = queue.length; i < len; ++i) {
3442                 var tween = queue[i];
3443                 if (!tween || !tween.isAnimated()) {
3444                     continue;
3445                 }
3446
3447                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3448                 {
3449                     tween.currentFrame += 1;
3450
3451                     if (tween.useSeconds) {
3452                         correctFrame(tween);
3453                     }
3454                     tween._onTween.fire();
3455                 }
3456                 else {
3457                     Roo.lib.AnimMgr.stop(tween, i);
3458                 }
3459             }
3460         };
3461
3462         var getIndex = function(anim) {
3463             for (var i = 0, len = queue.length; i < len; ++i) {
3464                 if (queue[i] == anim) {
3465                     return i;
3466                 }
3467             }
3468             return -1;
3469         };
3470
3471
3472         var correctFrame = function(tween) {
3473             var frames = tween.totalFrames;
3474             var frame = tween.currentFrame;
3475             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3476             var elapsed = (new Date() - tween.getStartTime());
3477             var tweak = 0;
3478
3479             if (elapsed < tween.duration * 1000) {
3480                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3481             } else {
3482                 tweak = frames - (frame + 1);
3483             }
3484             if (tweak > 0 && isFinite(tweak)) {
3485                 if (tween.currentFrame + tweak >= frames) {
3486                     tweak = frames - (frame + 1);
3487                 }
3488
3489                 tween.currentFrame += tweak;
3490             }
3491         };
3492     };/*
3493  * Portions of this file are based on pieces of Yahoo User Interface Library
3494  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3495  * YUI licensed under the BSD License:
3496  * http://developer.yahoo.net/yui/license.txt
3497  * <script type="text/javascript">
3498  *
3499  */
3500 Roo.lib.Bezier = new function() {
3501
3502         this.getPosition = function(points, t) {
3503             var n = points.length;
3504             var tmp = [];
3505
3506             for (var i = 0; i < n; ++i) {
3507                 tmp[i] = [points[i][0], points[i][1]];
3508             }
3509
3510             for (var j = 1; j < n; ++j) {
3511                 for (i = 0; i < n - j; ++i) {
3512                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3513                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3514                 }
3515             }
3516
3517             return [ tmp[0][0], tmp[0][1] ];
3518
3519         };
3520     };/*
3521  * Portions of this file are based on pieces of Yahoo User Interface Library
3522  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3523  * YUI licensed under the BSD License:
3524  * http://developer.yahoo.net/yui/license.txt
3525  * <script type="text/javascript">
3526  *
3527  */
3528 (function() {
3529
3530     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3531         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3532     };
3533
3534     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3535
3536     var fly = Roo.lib.AnimBase.fly;
3537     var Y = Roo.lib;
3538     var superclass = Y.ColorAnim.superclass;
3539     var proto = Y.ColorAnim.prototype;
3540
3541     proto.toString = function() {
3542         var el = this.getEl();
3543         var id = el.id || el.tagName;
3544         return ("ColorAnim " + id);
3545     };
3546
3547     proto.patterns.color = /color$/i;
3548     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3549     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3550     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3551     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3552
3553
3554     proto.parseColor = function(s) {
3555         if (s.length == 3) {
3556             return s;
3557         }
3558
3559         var c = this.patterns.hex.exec(s);
3560         if (c && c.length == 4) {
3561             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3562         }
3563
3564         c = this.patterns.rgb.exec(s);
3565         if (c && c.length == 4) {
3566             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3567         }
3568
3569         c = this.patterns.hex3.exec(s);
3570         if (c && c.length == 4) {
3571             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3572         }
3573
3574         return null;
3575     };
3576     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3577     proto.getAttribute = function(attr) {
3578         var el = this.getEl();
3579         if (this.patterns.color.test(attr)) {
3580             var val = fly(el).getStyle(attr);
3581
3582             if (this.patterns.transparent.test(val)) {
3583                 var parent = el.parentNode;
3584                 val = fly(parent).getStyle(attr);
3585
3586                 while (parent && this.patterns.transparent.test(val)) {
3587                     parent = parent.parentNode;
3588                     val = fly(parent).getStyle(attr);
3589                     if (parent.tagName.toUpperCase() == 'HTML') {
3590                         val = '#fff';
3591                     }
3592                 }
3593             }
3594         } else {
3595             val = superclass.getAttribute.call(this, attr);
3596         }
3597
3598         return val;
3599     };
3600     proto.getAttribute = function(attr) {
3601         var el = this.getEl();
3602         if (this.patterns.color.test(attr)) {
3603             var val = fly(el).getStyle(attr);
3604
3605             if (this.patterns.transparent.test(val)) {
3606                 var parent = el.parentNode;
3607                 val = fly(parent).getStyle(attr);
3608
3609                 while (parent && this.patterns.transparent.test(val)) {
3610                     parent = parent.parentNode;
3611                     val = fly(parent).getStyle(attr);
3612                     if (parent.tagName.toUpperCase() == 'HTML') {
3613                         val = '#fff';
3614                     }
3615                 }
3616             }
3617         } else {
3618             val = superclass.getAttribute.call(this, attr);
3619         }
3620
3621         return val;
3622     };
3623
3624     proto.doMethod = function(attr, start, end) {
3625         var val;
3626
3627         if (this.patterns.color.test(attr)) {
3628             val = [];
3629             for (var i = 0, len = start.length; i < len; ++i) {
3630                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3631             }
3632
3633             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3634         }
3635         else {
3636             val = superclass.doMethod.call(this, attr, start, end);
3637         }
3638
3639         return val;
3640     };
3641
3642     proto.setRuntimeAttribute = function(attr) {
3643         superclass.setRuntimeAttribute.call(this, attr);
3644
3645         if (this.patterns.color.test(attr)) {
3646             var attributes = this.attributes;
3647             var start = this.parseColor(this.runtimeAttributes[attr].start);
3648             var end = this.parseColor(this.runtimeAttributes[attr].end);
3649
3650             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3651                 end = this.parseColor(attributes[attr].by);
3652
3653                 for (var i = 0, len = start.length; i < len; ++i) {
3654                     end[i] = start[i] + end[i];
3655                 }
3656             }
3657
3658             this.runtimeAttributes[attr].start = start;
3659             this.runtimeAttributes[attr].end = end;
3660         }
3661     };
3662 })();
3663
3664 /*
3665  * Portions of this file are based on pieces of Yahoo User Interface Library
3666  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3667  * YUI licensed under the BSD License:
3668  * http://developer.yahoo.net/yui/license.txt
3669  * <script type="text/javascript">
3670  *
3671  */
3672 Roo.lib.Easing = {
3673
3674
3675     easeNone: function (t, b, c, d) {
3676         return c * t / d + b;
3677     },
3678
3679
3680     easeIn: function (t, b, c, d) {
3681         return c * (t /= d) * t + b;
3682     },
3683
3684
3685     easeOut: function (t, b, c, d) {
3686         return -c * (t /= d) * (t - 2) + b;
3687     },
3688
3689
3690     easeBoth: function (t, b, c, d) {
3691         if ((t /= d / 2) < 1) {
3692             return c / 2 * t * t + b;
3693         }
3694
3695         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3696     },
3697
3698
3699     easeInStrong: function (t, b, c, d) {
3700         return c * (t /= d) * t * t * t + b;
3701     },
3702
3703
3704     easeOutStrong: function (t, b, c, d) {
3705         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3706     },
3707
3708
3709     easeBothStrong: function (t, b, c, d) {
3710         if ((t /= d / 2) < 1) {
3711             return c / 2 * t * t * t * t + b;
3712         }
3713
3714         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3715     },
3716
3717
3718
3719     elasticIn: function (t, b, c, d, a, p) {
3720         if (t == 0) {
3721             return b;
3722         }
3723         if ((t /= d) == 1) {
3724             return b + c;
3725         }
3726         if (!p) {
3727             p = d * .3;
3728         }
3729
3730         if (!a || a < Math.abs(c)) {
3731             a = c;
3732             var s = p / 4;
3733         }
3734         else {
3735             var s = p / (2 * Math.PI) * Math.asin(c / a);
3736         }
3737
3738         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3739     },
3740
3741
3742     elasticOut: function (t, b, c, d, a, p) {
3743         if (t == 0) {
3744             return b;
3745         }
3746         if ((t /= d) == 1) {
3747             return b + c;
3748         }
3749         if (!p) {
3750             p = d * .3;
3751         }
3752
3753         if (!a || a < Math.abs(c)) {
3754             a = c;
3755             var s = p / 4;
3756         }
3757         else {
3758             var s = p / (2 * Math.PI) * Math.asin(c / a);
3759         }
3760
3761         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3762     },
3763
3764
3765     elasticBoth: function (t, b, c, d, a, p) {
3766         if (t == 0) {
3767             return b;
3768         }
3769
3770         if ((t /= d / 2) == 2) {
3771             return b + c;
3772         }
3773
3774         if (!p) {
3775             p = d * (.3 * 1.5);
3776         }
3777
3778         if (!a || a < Math.abs(c)) {
3779             a = c;
3780             var s = p / 4;
3781         }
3782         else {
3783             var s = p / (2 * Math.PI) * Math.asin(c / a);
3784         }
3785
3786         if (t < 1) {
3787             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3788                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3789         }
3790         return a * Math.pow(2, -10 * (t -= 1)) *
3791                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3792     },
3793
3794
3795
3796     backIn: function (t, b, c, d, s) {
3797         if (typeof s == 'undefined') {
3798             s = 1.70158;
3799         }
3800         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3801     },
3802
3803
3804     backOut: function (t, b, c, d, s) {
3805         if (typeof s == 'undefined') {
3806             s = 1.70158;
3807         }
3808         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3809     },
3810
3811
3812     backBoth: function (t, b, c, d, s) {
3813         if (typeof s == 'undefined') {
3814             s = 1.70158;
3815         }
3816
3817         if ((t /= d / 2 ) < 1) {
3818             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3819         }
3820         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3821     },
3822
3823
3824     bounceIn: function (t, b, c, d) {
3825         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3826     },
3827
3828
3829     bounceOut: function (t, b, c, d) {
3830         if ((t /= d) < (1 / 2.75)) {
3831             return c * (7.5625 * t * t) + b;
3832         } else if (t < (2 / 2.75)) {
3833             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3834         } else if (t < (2.5 / 2.75)) {
3835             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3836         }
3837         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3838     },
3839
3840
3841     bounceBoth: function (t, b, c, d) {
3842         if (t < d / 2) {
3843             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3844         }
3845         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3846     }
3847 };/*
3848  * Portions of this file are based on pieces of Yahoo User Interface Library
3849  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3850  * YUI licensed under the BSD License:
3851  * http://developer.yahoo.net/yui/license.txt
3852  * <script type="text/javascript">
3853  *
3854  */
3855     (function() {
3856         Roo.lib.Motion = function(el, attributes, duration, method) {
3857             if (el) {
3858                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3859             }
3860         };
3861
3862         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3863
3864
3865         var Y = Roo.lib;
3866         var superclass = Y.Motion.superclass;
3867         var proto = Y.Motion.prototype;
3868
3869         proto.toString = function() {
3870             var el = this.getEl();
3871             var id = el.id || el.tagName;
3872             return ("Motion " + id);
3873         };
3874
3875         proto.patterns.points = /^points$/i;
3876
3877         proto.setAttribute = function(attr, val, unit) {
3878             if (this.patterns.points.test(attr)) {
3879                 unit = unit || 'px';
3880                 superclass.setAttribute.call(this, 'left', val[0], unit);
3881                 superclass.setAttribute.call(this, 'top', val[1], unit);
3882             } else {
3883                 superclass.setAttribute.call(this, attr, val, unit);
3884             }
3885         };
3886
3887         proto.getAttribute = function(attr) {
3888             if (this.patterns.points.test(attr)) {
3889                 var val = [
3890                         superclass.getAttribute.call(this, 'left'),
3891                         superclass.getAttribute.call(this, 'top')
3892                         ];
3893             } else {
3894                 val = superclass.getAttribute.call(this, attr);
3895             }
3896
3897             return val;
3898         };
3899
3900         proto.doMethod = function(attr, start, end) {
3901             var val = null;
3902
3903             if (this.patterns.points.test(attr)) {
3904                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3905                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3906             } else {
3907                 val = superclass.doMethod.call(this, attr, start, end);
3908             }
3909             return val;
3910         };
3911
3912         proto.setRuntimeAttribute = function(attr) {
3913             if (this.patterns.points.test(attr)) {
3914                 var el = this.getEl();
3915                 var attributes = this.attributes;
3916                 var start;
3917                 var control = attributes['points']['control'] || [];
3918                 var end;
3919                 var i, len;
3920
3921                 if (control.length > 0 && !(control[0] instanceof Array)) {
3922                     control = [control];
3923                 } else {
3924                     var tmp = [];
3925                     for (i = 0,len = control.length; i < len; ++i) {
3926                         tmp[i] = control[i];
3927                     }
3928                     control = tmp;
3929                 }
3930
3931                 Roo.fly(el).position();
3932
3933                 if (isset(attributes['points']['from'])) {
3934                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3935                 }
3936                 else {
3937                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3938                 }
3939
3940                 start = this.getAttribute('points');
3941
3942
3943                 if (isset(attributes['points']['to'])) {
3944                     end = translateValues.call(this, attributes['points']['to'], start);
3945
3946                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3947                     for (i = 0,len = control.length; i < len; ++i) {
3948                         control[i] = translateValues.call(this, control[i], start);
3949                     }
3950
3951
3952                 } else if (isset(attributes['points']['by'])) {
3953                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3954
3955                     for (i = 0,len = control.length; i < len; ++i) {
3956                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3957                     }
3958                 }
3959
3960                 this.runtimeAttributes[attr] = [start];
3961
3962                 if (control.length > 0) {
3963                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3964                 }
3965
3966                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3967             }
3968             else {
3969                 superclass.setRuntimeAttribute.call(this, attr);
3970             }
3971         };
3972
3973         var translateValues = function(val, start) {
3974             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3975             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3976
3977             return val;
3978         };
3979
3980         var isset = function(prop) {
3981             return (typeof prop !== 'undefined');
3982         };
3983     })();
3984 /*
3985  * Portions of this file are based on pieces of Yahoo User Interface Library
3986  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3987  * YUI licensed under the BSD License:
3988  * http://developer.yahoo.net/yui/license.txt
3989  * <script type="text/javascript">
3990  *
3991  */
3992     (function() {
3993         Roo.lib.Scroll = function(el, attributes, duration, method) {
3994             if (el) {
3995                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3996             }
3997         };
3998
3999         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4000
4001
4002         var Y = Roo.lib;
4003         var superclass = Y.Scroll.superclass;
4004         var proto = Y.Scroll.prototype;
4005
4006         proto.toString = function() {
4007             var el = this.getEl();
4008             var id = el.id || el.tagName;
4009             return ("Scroll " + id);
4010         };
4011
4012         proto.doMethod = function(attr, start, end) {
4013             var val = null;
4014
4015             if (attr == 'scroll') {
4016                 val = [
4017                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4018                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4019                         ];
4020
4021             } else {
4022                 val = superclass.doMethod.call(this, attr, start, end);
4023             }
4024             return val;
4025         };
4026
4027         proto.getAttribute = function(attr) {
4028             var val = null;
4029             var el = this.getEl();
4030
4031             if (attr == 'scroll') {
4032                 val = [ el.scrollLeft, el.scrollTop ];
4033             } else {
4034                 val = superclass.getAttribute.call(this, attr);
4035             }
4036
4037             return val;
4038         };
4039
4040         proto.setAttribute = function(attr, val, unit) {
4041             var el = this.getEl();
4042
4043             if (attr == 'scroll') {
4044                 el.scrollLeft = val[0];
4045                 el.scrollTop = val[1];
4046             } else {
4047                 superclass.setAttribute.call(this, attr, val, unit);
4048             }
4049         };
4050     })();
4051 /*
4052  * Based on:
4053  * Ext JS Library 1.1.1
4054  * Copyright(c) 2006-2007, Ext JS, LLC.
4055  *
4056  * Originally Released Under LGPL - original licence link has changed is not relivant.
4057  *
4058  * Fork - LGPL
4059  * <script type="text/javascript">
4060  */
4061
4062
4063 // nasty IE9 hack - what a pile of crap that is..
4064
4065  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4066     Range.prototype.createContextualFragment = function (html) {
4067         var doc = window.document;
4068         var container = doc.createElement("div");
4069         container.innerHTML = html;
4070         var frag = doc.createDocumentFragment(), n;
4071         while ((n = container.firstChild)) {
4072             frag.appendChild(n);
4073         }
4074         return frag;
4075     };
4076 }
4077
4078 /**
4079  * @class Roo.DomHelper
4080  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4081  * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4082  * @singleton
4083  */
4084 Roo.DomHelper = function(){
4085     var tempTableEl = null;
4086     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4087     var tableRe = /^table|tbody|tr|td$/i;
4088     var xmlns = {};
4089     // build as innerHTML where available
4090     /** @ignore */
4091     var createHtml = function(o){
4092         if(typeof o == 'string'){
4093             return o;
4094         }
4095         var b = "";
4096         if(!o.tag){
4097             o.tag = "div";
4098         }
4099         b += "<" + o.tag;
4100         for(var attr in o){
4101             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4102             if(attr == "style"){
4103                 var s = o["style"];
4104                 if(typeof s == "function"){
4105                     s = s.call();
4106                 }
4107                 if(typeof s == "string"){
4108                     b += ' style="' + s + '"';
4109                 }else if(typeof s == "object"){
4110                     b += ' style="';
4111                     for(var key in s){
4112                         if(typeof s[key] != "function"){
4113                             b += key + ":" + s[key] + ";";
4114                         }
4115                     }
4116                     b += '"';
4117                 }
4118             }else{
4119                 if(attr == "cls"){
4120                     b += ' class="' + o["cls"] + '"';
4121                 }else if(attr == "htmlFor"){
4122                     b += ' for="' + o["htmlFor"] + '"';
4123                 }else{
4124                     b += " " + attr + '="' + o[attr] + '"';
4125                 }
4126             }
4127         }
4128         if(emptyTags.test(o.tag)){
4129             b += "/>";
4130         }else{
4131             b += ">";
4132             var cn = o.children || o.cn;
4133             if(cn){
4134                 //http://bugs.kde.org/show_bug.cgi?id=71506
4135                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4136                     for(var i = 0, len = cn.length; i < len; i++) {
4137                         b += createHtml(cn[i], b);
4138                     }
4139                 }else{
4140                     b += createHtml(cn, b);
4141                 }
4142             }
4143             if(o.html){
4144                 b += o.html;
4145             }
4146             b += "</" + o.tag + ">";
4147         }
4148         return b;
4149     };
4150
4151     // build as dom
4152     /** @ignore */
4153     var createDom = function(o, parentNode){
4154          
4155         // defininition craeted..
4156         var ns = false;
4157         if (o.ns && o.ns != 'html') {
4158                
4159             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4160                 xmlns[o.ns] = o.xmlns;
4161                 ns = o.xmlns;
4162             }
4163             if (typeof(xmlns[o.ns]) == 'undefined') {
4164                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4165             }
4166             ns = xmlns[o.ns];
4167         }
4168         
4169         
4170         if (typeof(o) == 'string') {
4171             return parentNode.appendChild(document.createTextNode(o));
4172         }
4173         o.tag = o.tag || div;
4174         if (o.ns && Roo.isIE) {
4175             ns = false;
4176             o.tag = o.ns + ':' + o.tag;
4177             
4178         }
4179         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4180         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4181         for(var attr in o){
4182             
4183             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4184                     attr == "style" || typeof o[attr] == "function") continue;
4185                     
4186             if(attr=="cls" && Roo.isIE){
4187                 el.className = o["cls"];
4188             }else{
4189                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4190                 else el[attr] = o[attr];
4191             }
4192         }
4193         Roo.DomHelper.applyStyles(el, o.style);
4194         var cn = o.children || o.cn;
4195         if(cn){
4196             //http://bugs.kde.org/show_bug.cgi?id=71506
4197              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4198                 for(var i = 0, len = cn.length; i < len; i++) {
4199                     createDom(cn[i], el);
4200                 }
4201             }else{
4202                 createDom(cn, el);
4203             }
4204         }
4205         if(o.html){
4206             el.innerHTML = o.html;
4207         }
4208         if(parentNode){
4209            parentNode.appendChild(el);
4210         }
4211         return el;
4212     };
4213
4214     var ieTable = function(depth, s, h, e){
4215         tempTableEl.innerHTML = [s, h, e].join('');
4216         var i = -1, el = tempTableEl;
4217         while(++i < depth){
4218             el = el.firstChild;
4219         }
4220         return el;
4221     };
4222
4223     // kill repeat to save bytes
4224     var ts = '<table>',
4225         te = '</table>',
4226         tbs = ts+'<tbody>',
4227         tbe = '</tbody>'+te,
4228         trs = tbs + '<tr>',
4229         tre = '</tr>'+tbe;
4230
4231     /**
4232      * @ignore
4233      * Nasty code for IE's broken table implementation
4234      */
4235     var insertIntoTable = function(tag, where, el, html){
4236         if(!tempTableEl){
4237             tempTableEl = document.createElement('div');
4238         }
4239         var node;
4240         var before = null;
4241         if(tag == 'td'){
4242             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4243                 return;
4244             }
4245             if(where == 'beforebegin'){
4246                 before = el;
4247                 el = el.parentNode;
4248             } else{
4249                 before = el.nextSibling;
4250                 el = el.parentNode;
4251             }
4252             node = ieTable(4, trs, html, tre);
4253         }
4254         else if(tag == 'tr'){
4255             if(where == 'beforebegin'){
4256                 before = el;
4257                 el = el.parentNode;
4258                 node = ieTable(3, tbs, html, tbe);
4259             } else if(where == 'afterend'){
4260                 before = el.nextSibling;
4261                 el = el.parentNode;
4262                 node = ieTable(3, tbs, html, tbe);
4263             } else{ // INTO a TR
4264                 if(where == 'afterbegin'){
4265                     before = el.firstChild;
4266                 }
4267                 node = ieTable(4, trs, html, tre);
4268             }
4269         } else if(tag == 'tbody'){
4270             if(where == 'beforebegin'){
4271                 before = el;
4272                 el = el.parentNode;
4273                 node = ieTable(2, ts, html, te);
4274             } else if(where == 'afterend'){
4275                 before = el.nextSibling;
4276                 el = el.parentNode;
4277                 node = ieTable(2, ts, html, te);
4278             } else{
4279                 if(where == 'afterbegin'){
4280                     before = el.firstChild;
4281                 }
4282                 node = ieTable(3, tbs, html, tbe);
4283             }
4284         } else{ // TABLE
4285             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4286                 return;
4287             }
4288             if(where == 'afterbegin'){
4289                 before = el.firstChild;
4290             }
4291             node = ieTable(2, ts, html, te);
4292         }
4293         el.insertBefore(node, before);
4294         return node;
4295     };
4296
4297     return {
4298     /** True to force the use of DOM instead of html fragments @type Boolean */
4299     useDom : false,
4300
4301     /**
4302      * Returns the markup for the passed Element(s) config
4303      * @param {Object} o The Dom object spec (and children)
4304      * @return {String}
4305      */
4306     markup : function(o){
4307         return createHtml(o);
4308     },
4309
4310     /**
4311      * Applies a style specification to an element
4312      * @param {String/HTMLElement} el The element to apply styles to
4313      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4314      * a function which returns such a specification.
4315      */
4316     applyStyles : function(el, styles){
4317         if(styles){
4318            el = Roo.fly(el);
4319            if(typeof styles == "string"){
4320                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4321                var matches;
4322                while ((matches = re.exec(styles)) != null){
4323                    el.setStyle(matches[1], matches[2]);
4324                }
4325            }else if (typeof styles == "object"){
4326                for (var style in styles){
4327                   el.setStyle(style, styles[style]);
4328                }
4329            }else if (typeof styles == "function"){
4330                 Roo.DomHelper.applyStyles(el, styles.call());
4331            }
4332         }
4333     },
4334
4335     /**
4336      * Inserts an HTML fragment into the Dom
4337      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4338      * @param {HTMLElement} el The context element
4339      * @param {String} html The HTML fragmenet
4340      * @return {HTMLElement} The new node
4341      */
4342     insertHtml : function(where, el, html){
4343         where = where.toLowerCase();
4344         if(el.insertAdjacentHTML){
4345             if(tableRe.test(el.tagName)){
4346                 var rs;
4347                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4348                     return rs;
4349                 }
4350             }
4351             switch(where){
4352                 case "beforebegin":
4353                     el.insertAdjacentHTML('BeforeBegin', html);
4354                     return el.previousSibling;
4355                 case "afterbegin":
4356                     el.insertAdjacentHTML('AfterBegin', html);
4357                     return el.firstChild;
4358                 case "beforeend":
4359                     el.insertAdjacentHTML('BeforeEnd', html);
4360                     return el.lastChild;
4361                 case "afterend":
4362                     el.insertAdjacentHTML('AfterEnd', html);
4363                     return el.nextSibling;
4364             }
4365             throw 'Illegal insertion point -> "' + where + '"';
4366         }
4367         var range = el.ownerDocument.createRange();
4368         var frag;
4369         switch(where){
4370              case "beforebegin":
4371                 range.setStartBefore(el);
4372                 frag = range.createContextualFragment(html);
4373                 el.parentNode.insertBefore(frag, el);
4374                 return el.previousSibling;
4375              case "afterbegin":
4376                 if(el.firstChild){
4377                     range.setStartBefore(el.firstChild);
4378                     frag = range.createContextualFragment(html);
4379                     el.insertBefore(frag, el.firstChild);
4380                     return el.firstChild;
4381                 }else{
4382                     el.innerHTML = html;
4383                     return el.firstChild;
4384                 }
4385             case "beforeend":
4386                 if(el.lastChild){
4387                     range.setStartAfter(el.lastChild);
4388                     frag = range.createContextualFragment(html);
4389                     el.appendChild(frag);
4390                     return el.lastChild;
4391                 }else{
4392                     el.innerHTML = html;
4393                     return el.lastChild;
4394                 }
4395             case "afterend":
4396                 range.setStartAfter(el);
4397                 frag = range.createContextualFragment(html);
4398                 el.parentNode.insertBefore(frag, el.nextSibling);
4399                 return el.nextSibling;
4400             }
4401             throw 'Illegal insertion point -> "' + where + '"';
4402     },
4403
4404     /**
4405      * Creates new Dom element(s) and inserts them before el
4406      * @param {String/HTMLElement/Element} el The context element
4407      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4408      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4409      * @return {HTMLElement/Roo.Element} The new node
4410      */
4411     insertBefore : function(el, o, returnElement){
4412         return this.doInsert(el, o, returnElement, "beforeBegin");
4413     },
4414
4415     /**
4416      * Creates new Dom element(s) and inserts them after el
4417      * @param {String/HTMLElement/Element} el The context element
4418      * @param {Object} o The Dom object spec (and children)
4419      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4420      * @return {HTMLElement/Roo.Element} The new node
4421      */
4422     insertAfter : function(el, o, returnElement){
4423         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4424     },
4425
4426     /**
4427      * Creates new Dom element(s) and inserts them as the first child of el
4428      * @param {String/HTMLElement/Element} el The context element
4429      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4430      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4431      * @return {HTMLElement/Roo.Element} The new node
4432      */
4433     insertFirst : function(el, o, returnElement){
4434         return this.doInsert(el, o, returnElement, "afterBegin");
4435     },
4436
4437     // private
4438     doInsert : function(el, o, returnElement, pos, sibling){
4439         el = Roo.getDom(el);
4440         var newNode;
4441         if(this.useDom || o.ns){
4442             newNode = createDom(o, null);
4443             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4444         }else{
4445             var html = createHtml(o);
4446             newNode = this.insertHtml(pos, el, html);
4447         }
4448         return returnElement ? Roo.get(newNode, true) : newNode;
4449     },
4450
4451     /**
4452      * Creates new Dom element(s) and appends them to el
4453      * @param {String/HTMLElement/Element} el The context element
4454      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4455      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4456      * @return {HTMLElement/Roo.Element} The new node
4457      */
4458     append : function(el, o, returnElement){
4459         el = Roo.getDom(el);
4460         var newNode;
4461         if(this.useDom || o.ns){
4462             newNode = createDom(o, null);
4463             el.appendChild(newNode);
4464         }else{
4465             var html = createHtml(o);
4466             newNode = this.insertHtml("beforeEnd", el, html);
4467         }
4468         return returnElement ? Roo.get(newNode, true) : newNode;
4469     },
4470
4471     /**
4472      * Creates new Dom element(s) and overwrites the contents of el with them
4473      * @param {String/HTMLElement/Element} el The context element
4474      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4475      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4476      * @return {HTMLElement/Roo.Element} The new node
4477      */
4478     overwrite : function(el, o, returnElement){
4479         el = Roo.getDom(el);
4480         if (o.ns) {
4481           
4482             while (el.childNodes.length) {
4483                 el.removeChild(el.firstChild);
4484             }
4485             createDom(o, el);
4486         } else {
4487             el.innerHTML = createHtml(o);   
4488         }
4489         
4490         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4491     },
4492
4493     /**
4494      * Creates a new Roo.DomHelper.Template from the Dom object spec
4495      * @param {Object} o The Dom object spec (and children)
4496      * @return {Roo.DomHelper.Template} The new template
4497      */
4498     createTemplate : function(o){
4499         var html = createHtml(o);
4500         return new Roo.Template(html);
4501     }
4502     };
4503 }();
4504 /*
4505  * Based on:
4506  * Ext JS Library 1.1.1
4507  * Copyright(c) 2006-2007, Ext JS, LLC.
4508  *
4509  * Originally Released Under LGPL - original licence link has changed is not relivant.
4510  *
4511  * Fork - LGPL
4512  * <script type="text/javascript">
4513  */
4514  
4515 /**
4516 * @class Roo.Template
4517 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4518 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4519 * Usage:
4520 <pre><code>
4521 var t = new Roo.Template({
4522     html :  '&lt;div name="{id}"&gt;' + 
4523         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4524         '&lt;/div&gt;',
4525     myformat: function (value, allValues) {
4526         return 'XX' + value;
4527     }
4528 });
4529 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4530 </code></pre>
4531 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4532 * @constructor
4533 * @param {Object} cfg - Configuration object.
4534 */
4535 Roo.Template = function(cfg){
4536     // BC!
4537     if(cfg instanceof Array){
4538         cfg = cfg.join("");
4539     }else if(arguments.length > 1){
4540         cfg = Array.prototype.join.call(arguments, "");
4541     }
4542     
4543     
4544     if (typeof(cfg) == 'object') {
4545         Roo.apply(this,cfg)
4546     } else {
4547         // bc
4548         this.html = cfg;
4549     }
4550     
4551     
4552 };
4553 Roo.Template.prototype = {
4554     
4555     /**
4556      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4557      */
4558     html : '',
4559     /**
4560      * Returns an HTML fragment of this template with the specified values applied.
4561      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4562      * @return {String} The HTML fragment
4563      */
4564     applyTemplate : function(values){
4565         try {
4566             
4567             if(this.compiled){
4568                 return this.compiled(values);
4569             }
4570             var useF = this.disableFormats !== true;
4571             var fm = Roo.util.Format, tpl = this;
4572             var fn = function(m, name, format, args){
4573                 if(format && useF){
4574                     if(format.substr(0, 5) == "this."){
4575                         return tpl.call(format.substr(5), values[name], values);
4576                     }else{
4577                         if(args){
4578                             // quoted values are required for strings in compiled templates, 
4579                             // but for non compiled we need to strip them
4580                             // quoted reversed for jsmin
4581                             var re = /^\s*['"](.*)["']\s*$/;
4582                             args = args.split(',');
4583                             for(var i = 0, len = args.length; i < len; i++){
4584                                 args[i] = args[i].replace(re, "$1");
4585                             }
4586                             args = [values[name]].concat(args);
4587                         }else{
4588                             args = [values[name]];
4589                         }
4590                         return fm[format].apply(fm, args);
4591                     }
4592                 }else{
4593                     return values[name] !== undefined ? values[name] : "";
4594                 }
4595             };
4596             return this.html.replace(this.re, fn);
4597         } catch (e) {
4598             Roo.log(e);
4599             throw e;
4600         }
4601          
4602     },
4603     
4604     /**
4605      * Sets the HTML used as the template and optionally compiles it.
4606      * @param {String} html
4607      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4608      * @return {Roo.Template} this
4609      */
4610     set : function(html, compile){
4611         this.html = html;
4612         this.compiled = null;
4613         if(compile){
4614             this.compile();
4615         }
4616         return this;
4617     },
4618     
4619     /**
4620      * True to disable format functions (defaults to false)
4621      * @type Boolean
4622      */
4623     disableFormats : false,
4624     
4625     /**
4626     * The regular expression used to match template variables 
4627     * @type RegExp
4628     * @property 
4629     */
4630     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4631     
4632     /**
4633      * Compiles the template into an internal function, eliminating the RegEx overhead.
4634      * @return {Roo.Template} this
4635      */
4636     compile : function(){
4637         var fm = Roo.util.Format;
4638         var useF = this.disableFormats !== true;
4639         var sep = Roo.isGecko ? "+" : ",";
4640         var fn = function(m, name, format, args){
4641             if(format && useF){
4642                 args = args ? ',' + args : "";
4643                 if(format.substr(0, 5) != "this."){
4644                     format = "fm." + format + '(';
4645                 }else{
4646                     format = 'this.call("'+ format.substr(5) + '", ';
4647                     args = ", values";
4648                 }
4649             }else{
4650                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4651             }
4652             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4653         };
4654         var body;
4655         // branched to use + in gecko and [].join() in others
4656         if(Roo.isGecko){
4657             body = "this.compiled = function(values){ return '" +
4658                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4659                     "';};";
4660         }else{
4661             body = ["this.compiled = function(values){ return ['"];
4662             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4663             body.push("'].join('');};");
4664             body = body.join('');
4665         }
4666         /**
4667          * eval:var:values
4668          * eval:var:fm
4669          */
4670         eval(body);
4671         return this;
4672     },
4673     
4674     // private function used to call members
4675     call : function(fnName, value, allValues){
4676         return this[fnName](value, allValues);
4677     },
4678     
4679     /**
4680      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4681      * @param {String/HTMLElement/Roo.Element} el The context element
4682      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4683      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4684      * @return {HTMLElement/Roo.Element} The new node or Element
4685      */
4686     insertFirst: function(el, values, returnElement){
4687         return this.doInsert('afterBegin', el, values, returnElement);
4688     },
4689
4690     /**
4691      * Applies the supplied values to the template and inserts the new node(s) before el.
4692      * @param {String/HTMLElement/Roo.Element} el The context element
4693      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4694      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4695      * @return {HTMLElement/Roo.Element} The new node or Element
4696      */
4697     insertBefore: function(el, values, returnElement){
4698         return this.doInsert('beforeBegin', el, values, returnElement);
4699     },
4700
4701     /**
4702      * Applies the supplied values to the template and inserts the new node(s) after el.
4703      * @param {String/HTMLElement/Roo.Element} el The context element
4704      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4705      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4706      * @return {HTMLElement/Roo.Element} The new node or Element
4707      */
4708     insertAfter : function(el, values, returnElement){
4709         return this.doInsert('afterEnd', el, values, returnElement);
4710     },
4711     
4712     /**
4713      * Applies the supplied values to the template and appends the new node(s) to el.
4714      * @param {String/HTMLElement/Roo.Element} el The context element
4715      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4716      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4717      * @return {HTMLElement/Roo.Element} The new node or Element
4718      */
4719     append : function(el, values, returnElement){
4720         return this.doInsert('beforeEnd', el, values, returnElement);
4721     },
4722
4723     doInsert : function(where, el, values, returnEl){
4724         el = Roo.getDom(el);
4725         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4726         return returnEl ? Roo.get(newNode, true) : newNode;
4727     },
4728
4729     /**
4730      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4731      * @param {String/HTMLElement/Roo.Element} el The context element
4732      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4733      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4734      * @return {HTMLElement/Roo.Element} The new node or Element
4735      */
4736     overwrite : function(el, values, returnElement){
4737         el = Roo.getDom(el);
4738         el.innerHTML = this.applyTemplate(values);
4739         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4740     }
4741 };
4742 /**
4743  * Alias for {@link #applyTemplate}
4744  * @method
4745  */
4746 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4747
4748 // backwards compat
4749 Roo.DomHelper.Template = Roo.Template;
4750
4751 /**
4752  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4753  * @param {String/HTMLElement} el A DOM element or its id
4754  * @returns {Roo.Template} The created template
4755  * @static
4756  */
4757 Roo.Template.from = function(el){
4758     el = Roo.getDom(el);
4759     return new Roo.Template(el.value || el.innerHTML);
4760 };/*
4761  * Based on:
4762  * Ext JS Library 1.1.1
4763  * Copyright(c) 2006-2007, Ext JS, LLC.
4764  *
4765  * Originally Released Under LGPL - original licence link has changed is not relivant.
4766  *
4767  * Fork - LGPL
4768  * <script type="text/javascript">
4769  */
4770  
4771
4772 /*
4773  * This is code is also distributed under MIT license for use
4774  * with jQuery and prototype JavaScript libraries.
4775  */
4776 /**
4777  * @class Roo.DomQuery
4778 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4779 <p>
4780 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4781
4782 <p>
4783 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4784 </p>
4785 <h4>Element Selectors:</h4>
4786 <ul class="list">
4787     <li> <b>*</b> any element</li>
4788     <li> <b>E</b> an element with the tag E</li>
4789     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4790     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4791     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4792     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4793 </ul>
4794 <h4>Attribute Selectors:</h4>
4795 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4796 <ul class="list">
4797     <li> <b>E[foo]</b> has an attribute "foo"</li>
4798     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4799     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4800     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4801     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4802     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4803     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4804 </ul>
4805 <h4>Pseudo Classes:</h4>
4806 <ul class="list">
4807     <li> <b>E:first-child</b> E is the first child of its parent</li>
4808     <li> <b>E:last-child</b> E is the last child of its parent</li>
4809     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4810     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4811     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4812     <li> <b>E:only-child</b> E is the only child of its parent</li>
4813     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4814     <li> <b>E:first</b> the first E in the resultset</li>
4815     <li> <b>E:last</b> the last E in the resultset</li>
4816     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4817     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4818     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4819     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4820     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4821     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4822     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4823     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4824     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4825 </ul>
4826 <h4>CSS Value Selectors:</h4>
4827 <ul class="list">
4828     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4829     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4830     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4831     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4832     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4833     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4834 </ul>
4835  * @singleton
4836  */
4837 Roo.DomQuery = function(){
4838     var cache = {}, simpleCache = {}, valueCache = {};
4839     var nonSpace = /\S/;
4840     var trimRe = /^\s+|\s+$/g;
4841     var tplRe = /\{(\d+)\}/g;
4842     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4843     var tagTokenRe = /^(#)?([\w-\*]+)/;
4844     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4845
4846     function child(p, index){
4847         var i = 0;
4848         var n = p.firstChild;
4849         while(n){
4850             if(n.nodeType == 1){
4851                if(++i == index){
4852                    return n;
4853                }
4854             }
4855             n = n.nextSibling;
4856         }
4857         return null;
4858     };
4859
4860     function next(n){
4861         while((n = n.nextSibling) && n.nodeType != 1);
4862         return n;
4863     };
4864
4865     function prev(n){
4866         while((n = n.previousSibling) && n.nodeType != 1);
4867         return n;
4868     };
4869
4870     function children(d){
4871         var n = d.firstChild, ni = -1;
4872             while(n){
4873                 var nx = n.nextSibling;
4874                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4875                     d.removeChild(n);
4876                 }else{
4877                     n.nodeIndex = ++ni;
4878                 }
4879                 n = nx;
4880             }
4881             return this;
4882         };
4883
4884     function byClassName(c, a, v){
4885         if(!v){
4886             return c;
4887         }
4888         var r = [], ri = -1, cn;
4889         for(var i = 0, ci; ci = c[i]; i++){
4890             if((' '+ci.className+' ').indexOf(v) != -1){
4891                 r[++ri] = ci;
4892             }
4893         }
4894         return r;
4895     };
4896
4897     function attrValue(n, attr){
4898         if(!n.tagName && typeof n.length != "undefined"){
4899             n = n[0];
4900         }
4901         if(!n){
4902             return null;
4903         }
4904         if(attr == "for"){
4905             return n.htmlFor;
4906         }
4907         if(attr == "class" || attr == "className"){
4908             return n.className;
4909         }
4910         return n.getAttribute(attr) || n[attr];
4911
4912     };
4913
4914     function getNodes(ns, mode, tagName){
4915         var result = [], ri = -1, cs;
4916         if(!ns){
4917             return result;
4918         }
4919         tagName = tagName || "*";
4920         if(typeof ns.getElementsByTagName != "undefined"){
4921             ns = [ns];
4922         }
4923         if(!mode){
4924             for(var i = 0, ni; ni = ns[i]; i++){
4925                 cs = ni.getElementsByTagName(tagName);
4926                 for(var j = 0, ci; ci = cs[j]; j++){
4927                     result[++ri] = ci;
4928                 }
4929             }
4930         }else if(mode == "/" || mode == ">"){
4931             var utag = tagName.toUpperCase();
4932             for(var i = 0, ni, cn; ni = ns[i]; i++){
4933                 cn = ni.children || ni.childNodes;
4934                 for(var j = 0, cj; cj = cn[j]; j++){
4935                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4936                         result[++ri] = cj;
4937                     }
4938                 }
4939             }
4940         }else if(mode == "+"){
4941             var utag = tagName.toUpperCase();
4942             for(var i = 0, n; n = ns[i]; i++){
4943                 while((n = n.nextSibling) && n.nodeType != 1);
4944                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4945                     result[++ri] = n;
4946                 }
4947             }
4948         }else if(mode == "~"){
4949             for(var i = 0, n; n = ns[i]; i++){
4950                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4951                 if(n){
4952                     result[++ri] = n;
4953                 }
4954             }
4955         }
4956         return result;
4957     };
4958
4959     function concat(a, b){
4960         if(b.slice){
4961             return a.concat(b);
4962         }
4963         for(var i = 0, l = b.length; i < l; i++){
4964             a[a.length] = b[i];
4965         }
4966         return a;
4967     }
4968
4969     function byTag(cs, tagName){
4970         if(cs.tagName || cs == document){
4971             cs = [cs];
4972         }
4973         if(!tagName){
4974             return cs;
4975         }
4976         var r = [], ri = -1;
4977         tagName = tagName.toLowerCase();
4978         for(var i = 0, ci; ci = cs[i]; i++){
4979             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4980                 r[++ri] = ci;
4981             }
4982         }
4983         return r;
4984     };
4985
4986     function byId(cs, attr, id){
4987         if(cs.tagName || cs == document){
4988             cs = [cs];
4989         }
4990         if(!id){
4991             return cs;
4992         }
4993         var r = [], ri = -1;
4994         for(var i = 0,ci; ci = cs[i]; i++){
4995             if(ci && ci.id == id){
4996                 r[++ri] = ci;
4997                 return r;
4998             }
4999         }
5000         return r;
5001     };
5002
5003     function byAttribute(cs, attr, value, op, custom){
5004         var r = [], ri = -1, st = custom=="{";
5005         var f = Roo.DomQuery.operators[op];
5006         for(var i = 0, ci; ci = cs[i]; i++){
5007             var a;
5008             if(st){
5009                 a = Roo.DomQuery.getStyle(ci, attr);
5010             }
5011             else if(attr == "class" || attr == "className"){
5012                 a = ci.className;
5013             }else if(attr == "for"){
5014                 a = ci.htmlFor;
5015             }else if(attr == "href"){
5016                 a = ci.getAttribute("href", 2);
5017             }else{
5018                 a = ci.getAttribute(attr);
5019             }
5020             if((f && f(a, value)) || (!f && a)){
5021                 r[++ri] = ci;
5022             }
5023         }
5024         return r;
5025     };
5026
5027     function byPseudo(cs, name, value){
5028         return Roo.DomQuery.pseudos[name](cs, value);
5029     };
5030
5031     // This is for IE MSXML which does not support expandos.
5032     // IE runs the same speed using setAttribute, however FF slows way down
5033     // and Safari completely fails so they need to continue to use expandos.
5034     var isIE = window.ActiveXObject ? true : false;
5035
5036     // this eval is stop the compressor from
5037     // renaming the variable to something shorter
5038     
5039     /** eval:var:batch */
5040     var batch = 30803; 
5041
5042     var key = 30803;
5043
5044     function nodupIEXml(cs){
5045         var d = ++key;
5046         cs[0].setAttribute("_nodup", d);
5047         var r = [cs[0]];
5048         for(var i = 1, len = cs.length; i < len; i++){
5049             var c = cs[i];
5050             if(!c.getAttribute("_nodup") != d){
5051                 c.setAttribute("_nodup", d);
5052                 r[r.length] = c;
5053             }
5054         }
5055         for(var i = 0, len = cs.length; i < len; i++){
5056             cs[i].removeAttribute("_nodup");
5057         }
5058         return r;
5059     }
5060
5061     function nodup(cs){
5062         if(!cs){
5063             return [];
5064         }
5065         var len = cs.length, c, i, r = cs, cj, ri = -1;
5066         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5067             return cs;
5068         }
5069         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5070             return nodupIEXml(cs);
5071         }
5072         var d = ++key;
5073         cs[0]._nodup = d;
5074         for(i = 1; c = cs[i]; i++){
5075             if(c._nodup != d){
5076                 c._nodup = d;
5077             }else{
5078                 r = [];
5079                 for(var j = 0; j < i; j++){
5080                     r[++ri] = cs[j];
5081                 }
5082                 for(j = i+1; cj = cs[j]; j++){
5083                     if(cj._nodup != d){
5084                         cj._nodup = d;
5085                         r[++ri] = cj;
5086                     }
5087                 }
5088                 return r;
5089             }
5090         }
5091         return r;
5092     }
5093
5094     function quickDiffIEXml(c1, c2){
5095         var d = ++key;
5096         for(var i = 0, len = c1.length; i < len; i++){
5097             c1[i].setAttribute("_qdiff", d);
5098         }
5099         var r = [];
5100         for(var i = 0, len = c2.length; i < len; i++){
5101             if(c2[i].getAttribute("_qdiff") != d){
5102                 r[r.length] = c2[i];
5103             }
5104         }
5105         for(var i = 0, len = c1.length; i < len; i++){
5106            c1[i].removeAttribute("_qdiff");
5107         }
5108         return r;
5109     }
5110
5111     function quickDiff(c1, c2){
5112         var len1 = c1.length;
5113         if(!len1){
5114             return c2;
5115         }
5116         if(isIE && c1[0].selectSingleNode){
5117             return quickDiffIEXml(c1, c2);
5118         }
5119         var d = ++key;
5120         for(var i = 0; i < len1; i++){
5121             c1[i]._qdiff = d;
5122         }
5123         var r = [];
5124         for(var i = 0, len = c2.length; i < len; i++){
5125             if(c2[i]._qdiff != d){
5126                 r[r.length] = c2[i];
5127             }
5128         }
5129         return r;
5130     }
5131
5132     function quickId(ns, mode, root, id){
5133         if(ns == root){
5134            var d = root.ownerDocument || root;
5135            return d.getElementById(id);
5136         }
5137         ns = getNodes(ns, mode, "*");
5138         return byId(ns, null, id);
5139     }
5140
5141     return {
5142         getStyle : function(el, name){
5143             return Roo.fly(el).getStyle(name);
5144         },
5145         /**
5146          * Compiles a selector/xpath query into a reusable function. The returned function
5147          * takes one parameter "root" (optional), which is the context node from where the query should start.
5148          * @param {String} selector The selector/xpath query
5149          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5150          * @return {Function}
5151          */
5152         compile : function(path, type){
5153             type = type || "select";
5154             
5155             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5156             var q = path, mode, lq;
5157             var tk = Roo.DomQuery.matchers;
5158             var tklen = tk.length;
5159             var mm;
5160
5161             // accept leading mode switch
5162             var lmode = q.match(modeRe);
5163             if(lmode && lmode[1]){
5164                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5165                 q = q.replace(lmode[1], "");
5166             }
5167             // strip leading slashes
5168             while(path.substr(0, 1)=="/"){
5169                 path = path.substr(1);
5170             }
5171
5172             while(q && lq != q){
5173                 lq = q;
5174                 var tm = q.match(tagTokenRe);
5175                 if(type == "select"){
5176                     if(tm){
5177                         if(tm[1] == "#"){
5178                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5179                         }else{
5180                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5181                         }
5182                         q = q.replace(tm[0], "");
5183                     }else if(q.substr(0, 1) != '@'){
5184                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5185                     }
5186                 }else{
5187                     if(tm){
5188                         if(tm[1] == "#"){
5189                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5190                         }else{
5191                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5192                         }
5193                         q = q.replace(tm[0], "");
5194                     }
5195                 }
5196                 while(!(mm = q.match(modeRe))){
5197                     var matched = false;
5198                     for(var j = 0; j < tklen; j++){
5199                         var t = tk[j];
5200                         var m = q.match(t.re);
5201                         if(m){
5202                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5203                                                     return m[i];
5204                                                 });
5205                             q = q.replace(m[0], "");
5206                             matched = true;
5207                             break;
5208                         }
5209                     }
5210                     // prevent infinite loop on bad selector
5211                     if(!matched){
5212                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5213                     }
5214                 }
5215                 if(mm[1]){
5216                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5217                     q = q.replace(mm[1], "");
5218                 }
5219             }
5220             fn[fn.length] = "return nodup(n);\n}";
5221             
5222              /** 
5223               * list of variables that need from compression as they are used by eval.
5224              *  eval:var:batch 
5225              *  eval:var:nodup
5226              *  eval:var:byTag
5227              *  eval:var:ById
5228              *  eval:var:getNodes
5229              *  eval:var:quickId
5230              *  eval:var:mode
5231              *  eval:var:root
5232              *  eval:var:n
5233              *  eval:var:byClassName
5234              *  eval:var:byPseudo
5235              *  eval:var:byAttribute
5236              *  eval:var:attrValue
5237              * 
5238              **/ 
5239             eval(fn.join(""));
5240             return f;
5241         },
5242
5243         /**
5244          * Selects a group of elements.
5245          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5246          * @param {Node} root (optional) The start of the query (defaults to document).
5247          * @return {Array}
5248          */
5249         select : function(path, root, type){
5250             if(!root || root == document){
5251                 root = document;
5252             }
5253             if(typeof root == "string"){
5254                 root = document.getElementById(root);
5255             }
5256             var paths = path.split(",");
5257             var results = [];
5258             for(var i = 0, len = paths.length; i < len; i++){
5259                 var p = paths[i].replace(trimRe, "");
5260                 if(!cache[p]){
5261                     cache[p] = Roo.DomQuery.compile(p);
5262                     if(!cache[p]){
5263                         throw p + " is not a valid selector";
5264                     }
5265                 }
5266                 var result = cache[p](root);
5267                 if(result && result != document){
5268                     results = results.concat(result);
5269                 }
5270             }
5271             if(paths.length > 1){
5272                 return nodup(results);
5273             }
5274             return results;
5275         },
5276
5277         /**
5278          * Selects a single element.
5279          * @param {String} selector The selector/xpath query
5280          * @param {Node} root (optional) The start of the query (defaults to document).
5281          * @return {Element}
5282          */
5283         selectNode : function(path, root){
5284             return Roo.DomQuery.select(path, root)[0];
5285         },
5286
5287         /**
5288          * Selects the value of a node, optionally replacing null with the defaultValue.
5289          * @param {String} selector The selector/xpath query
5290          * @param {Node} root (optional) The start of the query (defaults to document).
5291          * @param {String} defaultValue
5292          */
5293         selectValue : function(path, root, defaultValue){
5294             path = path.replace(trimRe, "");
5295             if(!valueCache[path]){
5296                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5297             }
5298             var n = valueCache[path](root);
5299             n = n[0] ? n[0] : n;
5300             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5301             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5302         },
5303
5304         /**
5305          * Selects the value of a node, parsing integers and floats.
5306          * @param {String} selector The selector/xpath query
5307          * @param {Node} root (optional) The start of the query (defaults to document).
5308          * @param {Number} defaultValue
5309          * @return {Number}
5310          */
5311         selectNumber : function(path, root, defaultValue){
5312             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5313             return parseFloat(v);
5314         },
5315
5316         /**
5317          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5318          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5319          * @param {String} selector The simple selector to test
5320          * @return {Boolean}
5321          */
5322         is : function(el, ss){
5323             if(typeof el == "string"){
5324                 el = document.getElementById(el);
5325             }
5326             var isArray = (el instanceof Array);
5327             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5328             return isArray ? (result.length == el.length) : (result.length > 0);
5329         },
5330
5331         /**
5332          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5333          * @param {Array} el An array of elements to filter
5334          * @param {String} selector The simple selector to test
5335          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5336          * the selector instead of the ones that match
5337          * @return {Array}
5338          */
5339         filter : function(els, ss, nonMatches){
5340             ss = ss.replace(trimRe, "");
5341             if(!simpleCache[ss]){
5342                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5343             }
5344             var result = simpleCache[ss](els);
5345             return nonMatches ? quickDiff(result, els) : result;
5346         },
5347
5348         /**
5349          * Collection of matching regular expressions and code snippets.
5350          */
5351         matchers : [{
5352                 re: /^\.([\w-]+)/,
5353                 select: 'n = byClassName(n, null, " {1} ");'
5354             }, {
5355                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5356                 select: 'n = byPseudo(n, "{1}", "{2}");'
5357             },{
5358                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5359                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5360             }, {
5361                 re: /^#([\w-]+)/,
5362                 select: 'n = byId(n, null, "{1}");'
5363             },{
5364                 re: /^@([\w-]+)/,
5365                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5366             }
5367         ],
5368
5369         /**
5370          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5371          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5372          */
5373         operators : {
5374             "=" : function(a, v){
5375                 return a == v;
5376             },
5377             "!=" : function(a, v){
5378                 return a != v;
5379             },
5380             "^=" : function(a, v){
5381                 return a && a.substr(0, v.length) == v;
5382             },
5383             "$=" : function(a, v){
5384                 return a && a.substr(a.length-v.length) == v;
5385             },
5386             "*=" : function(a, v){
5387                 return a && a.indexOf(v) !== -1;
5388             },
5389             "%=" : function(a, v){
5390                 return (a % v) == 0;
5391             },
5392             "|=" : function(a, v){
5393                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5394             },
5395             "~=" : function(a, v){
5396                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5397             }
5398         },
5399
5400         /**
5401          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5402          * and the argument (if any) supplied in the selector.
5403          */
5404         pseudos : {
5405             "first-child" : function(c){
5406                 var r = [], ri = -1, n;
5407                 for(var i = 0, ci; ci = n = c[i]; i++){
5408                     while((n = n.previousSibling) && n.nodeType != 1);
5409                     if(!n){
5410                         r[++ri] = ci;
5411                     }
5412                 }
5413                 return r;
5414             },
5415
5416             "last-child" : function(c){
5417                 var r = [], ri = -1, n;
5418                 for(var i = 0, ci; ci = n = c[i]; i++){
5419                     while((n = n.nextSibling) && n.nodeType != 1);
5420                     if(!n){
5421                         r[++ri] = ci;
5422                     }
5423                 }
5424                 return r;
5425             },
5426
5427             "nth-child" : function(c, a) {
5428                 var r = [], ri = -1;
5429                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5430                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5431                 for(var i = 0, n; n = c[i]; i++){
5432                     var pn = n.parentNode;
5433                     if (batch != pn._batch) {
5434                         var j = 0;
5435                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5436                             if(cn.nodeType == 1){
5437                                cn.nodeIndex = ++j;
5438                             }
5439                         }
5440                         pn._batch = batch;
5441                     }
5442                     if (f == 1) {
5443                         if (l == 0 || n.nodeIndex == l){
5444                             r[++ri] = n;
5445                         }
5446                     } else if ((n.nodeIndex + l) % f == 0){
5447                         r[++ri] = n;
5448                     }
5449                 }
5450
5451                 return r;
5452             },
5453
5454             "only-child" : function(c){
5455                 var r = [], ri = -1;;
5456                 for(var i = 0, ci; ci = c[i]; i++){
5457                     if(!prev(ci) && !next(ci)){
5458                         r[++ri] = ci;
5459                     }
5460                 }
5461                 return r;
5462             },
5463
5464             "empty" : function(c){
5465                 var r = [], ri = -1;
5466                 for(var i = 0, ci; ci = c[i]; i++){
5467                     var cns = ci.childNodes, j = 0, cn, empty = true;
5468                     while(cn = cns[j]){
5469                         ++j;
5470                         if(cn.nodeType == 1 || cn.nodeType == 3){
5471                             empty = false;
5472                             break;
5473                         }
5474                     }
5475                     if(empty){
5476                         r[++ri] = ci;
5477                     }
5478                 }
5479                 return r;
5480             },
5481
5482             "contains" : function(c, v){
5483                 var r = [], ri = -1;
5484                 for(var i = 0, ci; ci = c[i]; i++){
5485                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5486                         r[++ri] = ci;
5487                     }
5488                 }
5489                 return r;
5490             },
5491
5492             "nodeValue" : function(c, v){
5493                 var r = [], ri = -1;
5494                 for(var i = 0, ci; ci = c[i]; i++){
5495                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5496                         r[++ri] = ci;
5497                     }
5498                 }
5499                 return r;
5500             },
5501
5502             "checked" : function(c){
5503                 var r = [], ri = -1;
5504                 for(var i = 0, ci; ci = c[i]; i++){
5505                     if(ci.checked == true){
5506                         r[++ri] = ci;
5507                     }
5508                 }
5509                 return r;
5510             },
5511
5512             "not" : function(c, ss){
5513                 return Roo.DomQuery.filter(c, ss, true);
5514             },
5515
5516             "odd" : function(c){
5517                 return this["nth-child"](c, "odd");
5518             },
5519
5520             "even" : function(c){
5521                 return this["nth-child"](c, "even");
5522             },
5523
5524             "nth" : function(c, a){
5525                 return c[a-1] || [];
5526             },
5527
5528             "first" : function(c){
5529                 return c[0] || [];
5530             },
5531
5532             "last" : function(c){
5533                 return c[c.length-1] || [];
5534             },
5535
5536             "has" : function(c, ss){
5537                 var s = Roo.DomQuery.select;
5538                 var r = [], ri = -1;
5539                 for(var i = 0, ci; ci = c[i]; i++){
5540                     if(s(ss, ci).length > 0){
5541                         r[++ri] = ci;
5542                     }
5543                 }
5544                 return r;
5545             },
5546
5547             "next" : function(c, ss){
5548                 var is = Roo.DomQuery.is;
5549                 var r = [], ri = -1;
5550                 for(var i = 0, ci; ci = c[i]; i++){
5551                     var n = next(ci);
5552                     if(n && is(n, ss)){
5553                         r[++ri] = ci;
5554                     }
5555                 }
5556                 return r;
5557             },
5558
5559             "prev" : function(c, ss){
5560                 var is = Roo.DomQuery.is;
5561                 var r = [], ri = -1;
5562                 for(var i = 0, ci; ci = c[i]; i++){
5563                     var n = prev(ci);
5564                     if(n && is(n, ss)){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             }
5570         }
5571     };
5572 }();
5573
5574 /**
5575  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5576  * @param {String} path The selector/xpath query
5577  * @param {Node} root (optional) The start of the query (defaults to document).
5578  * @return {Array}
5579  * @member Roo
5580  * @method query
5581  */
5582 Roo.query = Roo.DomQuery.select;
5583 /*
5584  * Based on:
5585  * Ext JS Library 1.1.1
5586  * Copyright(c) 2006-2007, Ext JS, LLC.
5587  *
5588  * Originally Released Under LGPL - original licence link has changed is not relivant.
5589  *
5590  * Fork - LGPL
5591  * <script type="text/javascript">
5592  */
5593
5594 /**
5595  * @class Roo.util.Observable
5596  * Base class that provides a common interface for publishing events. Subclasses are expected to
5597  * to have a property "events" with all the events defined.<br>
5598  * For example:
5599  * <pre><code>
5600  Employee = function(name){
5601     this.name = name;
5602     this.addEvents({
5603         "fired" : true,
5604         "quit" : true
5605     });
5606  }
5607  Roo.extend(Employee, Roo.util.Observable);
5608 </code></pre>
5609  * @param {Object} config properties to use (incuding events / listeners)
5610  */
5611
5612 Roo.util.Observable = function(cfg){
5613     
5614     cfg = cfg|| {};
5615     this.addEvents(cfg.events || {});
5616     if (cfg.events) {
5617         delete cfg.events; // make sure
5618     }
5619      
5620     Roo.apply(this, cfg);
5621     
5622     if(this.listeners){
5623         this.on(this.listeners);
5624         delete this.listeners;
5625     }
5626 };
5627 Roo.util.Observable.prototype = {
5628     /** 
5629  * @cfg {Object} listeners  list of events and functions to call for this object, 
5630  * For example :
5631  * <pre><code>
5632     listeners :  { 
5633        'click' : function(e) {
5634            ..... 
5635         } ,
5636         .... 
5637     } 
5638   </code></pre>
5639  */
5640     
5641     
5642     /**
5643      * Fires the specified event with the passed parameters (minus the event name).
5644      * @param {String} eventName
5645      * @param {Object...} args Variable number of parameters are passed to handlers
5646      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5647      */
5648     fireEvent : function(){
5649         var ce = this.events[arguments[0].toLowerCase()];
5650         if(typeof ce == "object"){
5651             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5652         }else{
5653             return true;
5654         }
5655     },
5656
5657     // private
5658     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5659
5660     /**
5661      * Appends an event handler to this component
5662      * @param {String}   eventName The type of event to listen for
5663      * @param {Function} handler The method the event invokes
5664      * @param {Object}   scope (optional) The scope in which to execute the handler
5665      * function. The handler function's "this" context.
5666      * @param {Object}   options (optional) An object containing handler configuration
5667      * properties. This may contain any of the following properties:<ul>
5668      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5669      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5670      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5671      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5672      * by the specified number of milliseconds. If the event fires again within that time, the original
5673      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5674      * </ul><br>
5675      * <p>
5676      * <b>Combining Options</b><br>
5677      * Using the options argument, it is possible to combine different types of listeners:<br>
5678      * <br>
5679      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5680                 <pre><code>
5681                 el.on('click', this.onClick, this, {
5682                         single: true,
5683                 delay: 100,
5684                 forumId: 4
5685                 });
5686                 </code></pre>
5687      * <p>
5688      * <b>Attaching multiple handlers in 1 call</b><br>
5689      * The method also allows for a single argument to be passed which is a config object containing properties
5690      * which specify multiple handlers.
5691      * <pre><code>
5692                 el.on({
5693                         'click': {
5694                         fn: this.onClick,
5695                         scope: this,
5696                         delay: 100
5697                 }, 
5698                 'mouseover': {
5699                         fn: this.onMouseOver,
5700                         scope: this
5701                 },
5702                 'mouseout': {
5703                         fn: this.onMouseOut,
5704                         scope: this
5705                 }
5706                 });
5707                 </code></pre>
5708      * <p>
5709      * Or a shorthand syntax which passes the same scope object to all handlers:
5710         <pre><code>
5711                 el.on({
5712                         'click': this.onClick,
5713                 'mouseover': this.onMouseOver,
5714                 'mouseout': this.onMouseOut,
5715                 scope: this
5716                 });
5717                 </code></pre>
5718      */
5719     addListener : function(eventName, fn, scope, o){
5720         if(typeof eventName == "object"){
5721             o = eventName;
5722             for(var e in o){
5723                 if(this.filterOptRe.test(e)){
5724                     continue;
5725                 }
5726                 if(typeof o[e] == "function"){
5727                     // shared options
5728                     this.addListener(e, o[e], o.scope,  o);
5729                 }else{
5730                     // individual options
5731                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5732                 }
5733             }
5734             return;
5735         }
5736         o = (!o || typeof o == "boolean") ? {} : o;
5737         eventName = eventName.toLowerCase();
5738         var ce = this.events[eventName] || true;
5739         if(typeof ce == "boolean"){
5740             ce = new Roo.util.Event(this, eventName);
5741             this.events[eventName] = ce;
5742         }
5743         ce.addListener(fn, scope, o);
5744     },
5745
5746     /**
5747      * Removes a listener
5748      * @param {String}   eventName     The type of event to listen for
5749      * @param {Function} handler        The handler to remove
5750      * @param {Object}   scope  (optional) The scope (this object) for the handler
5751      */
5752     removeListener : function(eventName, fn, scope){
5753         var ce = this.events[eventName.toLowerCase()];
5754         if(typeof ce == "object"){
5755             ce.removeListener(fn, scope);
5756         }
5757     },
5758
5759     /**
5760      * Removes all listeners for this object
5761      */
5762     purgeListeners : function(){
5763         for(var evt in this.events){
5764             if(typeof this.events[evt] == "object"){
5765                  this.events[evt].clearListeners();
5766             }
5767         }
5768     },
5769
5770     relayEvents : function(o, events){
5771         var createHandler = function(ename){
5772             return function(){
5773                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5774             };
5775         };
5776         for(var i = 0, len = events.length; i < len; i++){
5777             var ename = events[i];
5778             if(!this.events[ename]){ this.events[ename] = true; };
5779             o.on(ename, createHandler(ename), this);
5780         }
5781     },
5782
5783     /**
5784      * Used to define events on this Observable
5785      * @param {Object} object The object with the events defined
5786      */
5787     addEvents : function(o){
5788         if(!this.events){
5789             this.events = {};
5790         }
5791         Roo.applyIf(this.events, o);
5792     },
5793
5794     /**
5795      * Checks to see if this object has any listeners for a specified event
5796      * @param {String} eventName The name of the event to check for
5797      * @return {Boolean} True if the event is being listened for, else false
5798      */
5799     hasListener : function(eventName){
5800         var e = this.events[eventName];
5801         return typeof e == "object" && e.listeners.length > 0;
5802     }
5803 };
5804 /**
5805  * Appends an event handler to this element (shorthand for addListener)
5806  * @param {String}   eventName     The type of event to listen for
5807  * @param {Function} handler        The method the event invokes
5808  * @param {Object}   scope (optional) The scope in which to execute the handler
5809  * function. The handler function's "this" context.
5810  * @param {Object}   options  (optional)
5811  * @method
5812  */
5813 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5814 /**
5815  * Removes a listener (shorthand for removeListener)
5816  * @param {String}   eventName     The type of event to listen for
5817  * @param {Function} handler        The handler to remove
5818  * @param {Object}   scope  (optional) The scope (this object) for the handler
5819  * @method
5820  */
5821 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5822
5823 /**
5824  * Starts capture on the specified Observable. All events will be passed
5825  * to the supplied function with the event name + standard signature of the event
5826  * <b>before</b> the event is fired. If the supplied function returns false,
5827  * the event will not fire.
5828  * @param {Observable} o The Observable to capture
5829  * @param {Function} fn The function to call
5830  * @param {Object} scope (optional) The scope (this object) for the fn
5831  * @static
5832  */
5833 Roo.util.Observable.capture = function(o, fn, scope){
5834     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5835 };
5836
5837 /**
5838  * Removes <b>all</b> added captures from the Observable.
5839  * @param {Observable} o The Observable to release
5840  * @static
5841  */
5842 Roo.util.Observable.releaseCapture = function(o){
5843     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5844 };
5845
5846 (function(){
5847
5848     var createBuffered = function(h, o, scope){
5849         var task = new Roo.util.DelayedTask();
5850         return function(){
5851             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5852         };
5853     };
5854
5855     var createSingle = function(h, e, fn, scope){
5856         return function(){
5857             e.removeListener(fn, scope);
5858             return h.apply(scope, arguments);
5859         };
5860     };
5861
5862     var createDelayed = function(h, o, scope){
5863         return function(){
5864             var args = Array.prototype.slice.call(arguments, 0);
5865             setTimeout(function(){
5866                 h.apply(scope, args);
5867             }, o.delay || 10);
5868         };
5869     };
5870
5871     Roo.util.Event = function(obj, name){
5872         this.name = name;
5873         this.obj = obj;
5874         this.listeners = [];
5875     };
5876
5877     Roo.util.Event.prototype = {
5878         addListener : function(fn, scope, options){
5879             var o = options || {};
5880             scope = scope || this.obj;
5881             if(!this.isListening(fn, scope)){
5882                 var l = {fn: fn, scope: scope, options: o};
5883                 var h = fn;
5884                 if(o.delay){
5885                     h = createDelayed(h, o, scope);
5886                 }
5887                 if(o.single){
5888                     h = createSingle(h, this, fn, scope);
5889                 }
5890                 if(o.buffer){
5891                     h = createBuffered(h, o, scope);
5892                 }
5893                 l.fireFn = h;
5894                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5895                     this.listeners.push(l);
5896                 }else{
5897                     this.listeners = this.listeners.slice(0);
5898                     this.listeners.push(l);
5899                 }
5900             }
5901         },
5902
5903         findListener : function(fn, scope){
5904             scope = scope || this.obj;
5905             var ls = this.listeners;
5906             for(var i = 0, len = ls.length; i < len; i++){
5907                 var l = ls[i];
5908                 if(l.fn == fn && l.scope == scope){
5909                     return i;
5910                 }
5911             }
5912             return -1;
5913         },
5914
5915         isListening : function(fn, scope){
5916             return this.findListener(fn, scope) != -1;
5917         },
5918
5919         removeListener : function(fn, scope){
5920             var index;
5921             if((index = this.findListener(fn, scope)) != -1){
5922                 if(!this.firing){
5923                     this.listeners.splice(index, 1);
5924                 }else{
5925                     this.listeners = this.listeners.slice(0);
5926                     this.listeners.splice(index, 1);
5927                 }
5928                 return true;
5929             }
5930             return false;
5931         },
5932
5933         clearListeners : function(){
5934             this.listeners = [];
5935         },
5936
5937         fire : function(){
5938             var ls = this.listeners, scope, len = ls.length;
5939             if(len > 0){
5940                 this.firing = true;
5941                 var args = Array.prototype.slice.call(arguments, 0);
5942                 for(var i = 0; i < len; i++){
5943                     var l = ls[i];
5944                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5945                         this.firing = false;
5946                         return false;
5947                     }
5948                 }
5949                 this.firing = false;
5950             }
5951             return true;
5952         }
5953     };
5954 })();/*
5955  * Based on:
5956  * Ext JS Library 1.1.1
5957  * Copyright(c) 2006-2007, Ext JS, LLC.
5958  *
5959  * Originally Released Under LGPL - original licence link has changed is not relivant.
5960  *
5961  * Fork - LGPL
5962  * <script type="text/javascript">
5963  */
5964
5965 /**
5966  * @class Roo.EventManager
5967  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5968  * several useful events directly.
5969  * See {@link Roo.EventObject} for more details on normalized event objects.
5970  * @singleton
5971  */
5972 Roo.EventManager = function(){
5973     var docReadyEvent, docReadyProcId, docReadyState = false;
5974     var resizeEvent, resizeTask, textEvent, textSize;
5975     var E = Roo.lib.Event;
5976     var D = Roo.lib.Dom;
5977
5978
5979     var fireDocReady = function(){
5980         if(!docReadyState){
5981             docReadyState = true;
5982             Roo.isReady = true;
5983             if(docReadyProcId){
5984                 clearInterval(docReadyProcId);
5985             }
5986             if(Roo.isGecko || Roo.isOpera) {
5987                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5988             }
5989             if(Roo.isIE){
5990                 var defer = document.getElementById("ie-deferred-loader");
5991                 if(defer){
5992                     defer.onreadystatechange = null;
5993                     defer.parentNode.removeChild(defer);
5994                 }
5995             }
5996             if(docReadyEvent){
5997                 docReadyEvent.fire();
5998                 docReadyEvent.clearListeners();
5999             }
6000         }
6001     };
6002     
6003     var initDocReady = function(){
6004         docReadyEvent = new Roo.util.Event();
6005         if(Roo.isGecko || Roo.isOpera) {
6006             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6007         }else if(Roo.isIE){
6008             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6009             var defer = document.getElementById("ie-deferred-loader");
6010             defer.onreadystatechange = function(){
6011                 if(this.readyState == "complete"){
6012                     fireDocReady();
6013                 }
6014             };
6015         }else if(Roo.isSafari){ 
6016             docReadyProcId = setInterval(function(){
6017                 var rs = document.readyState;
6018                 if(rs == "complete") {
6019                     fireDocReady();     
6020                  }
6021             }, 10);
6022         }
6023         // no matter what, make sure it fires on load
6024         E.on(window, "load", fireDocReady);
6025     };
6026
6027     var createBuffered = function(h, o){
6028         var task = new Roo.util.DelayedTask(h);
6029         return function(e){
6030             // create new event object impl so new events don't wipe out properties
6031             e = new Roo.EventObjectImpl(e);
6032             task.delay(o.buffer, h, null, [e]);
6033         };
6034     };
6035
6036     var createSingle = function(h, el, ename, fn){
6037         return function(e){
6038             Roo.EventManager.removeListener(el, ename, fn);
6039             h(e);
6040         };
6041     };
6042
6043     var createDelayed = function(h, o){
6044         return function(e){
6045             // create new event object impl so new events don't wipe out properties
6046             e = new Roo.EventObjectImpl(e);
6047             setTimeout(function(){
6048                 h(e);
6049             }, o.delay || 10);
6050         };
6051     };
6052
6053     var listen = function(element, ename, opt, fn, scope){
6054         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6055         fn = fn || o.fn; scope = scope || o.scope;
6056         var el = Roo.getDom(element);
6057         if(!el){
6058             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6059         }
6060         var h = function(e){
6061             e = Roo.EventObject.setEvent(e);
6062             var t;
6063             if(o.delegate){
6064                 t = e.getTarget(o.delegate, el);
6065                 if(!t){
6066                     return;
6067                 }
6068             }else{
6069                 t = e.target;
6070             }
6071             if(o.stopEvent === true){
6072                 e.stopEvent();
6073             }
6074             if(o.preventDefault === true){
6075                e.preventDefault();
6076             }
6077             if(o.stopPropagation === true){
6078                 e.stopPropagation();
6079             }
6080
6081             if(o.normalized === false){
6082                 e = e.browserEvent;
6083             }
6084
6085             fn.call(scope || el, e, t, o);
6086         };
6087         if(o.delay){
6088             h = createDelayed(h, o);
6089         }
6090         if(o.single){
6091             h = createSingle(h, el, ename, fn);
6092         }
6093         if(o.buffer){
6094             h = createBuffered(h, o);
6095         }
6096         fn._handlers = fn._handlers || [];
6097         fn._handlers.push([Roo.id(el), ename, h]);
6098
6099         E.on(el, ename, h);
6100         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6101             el.addEventListener("DOMMouseScroll", h, false);
6102             E.on(window, 'unload', function(){
6103                 el.removeEventListener("DOMMouseScroll", h, false);
6104             });
6105         }
6106         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6107             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6108         }
6109         return h;
6110     };
6111
6112     var stopListening = function(el, ename, fn){
6113         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6114         if(hds){
6115             for(var i = 0, len = hds.length; i < len; i++){
6116                 var h = hds[i];
6117                 if(h[0] == id && h[1] == ename){
6118                     hd = h[2];
6119                     hds.splice(i, 1);
6120                     break;
6121                 }
6122             }
6123         }
6124         E.un(el, ename, hd);
6125         el = Roo.getDom(el);
6126         if(ename == "mousewheel" && el.addEventListener){
6127             el.removeEventListener("DOMMouseScroll", hd, false);
6128         }
6129         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6130             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6131         }
6132     };
6133
6134     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6135     
6136     var pub = {
6137         
6138         
6139         /** 
6140          * Fix for doc tools
6141          * @scope Roo.EventManager
6142          */
6143         
6144         
6145         /** 
6146          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6147          * object with a Roo.EventObject
6148          * @param {Function} fn        The method the event invokes
6149          * @param {Object}   scope    An object that becomes the scope of the handler
6150          * @param {boolean}  override If true, the obj passed in becomes
6151          *                             the execution scope of the listener
6152          * @return {Function} The wrapped function
6153          * @deprecated
6154          */
6155         wrap : function(fn, scope, override){
6156             return function(e){
6157                 Roo.EventObject.setEvent(e);
6158                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6159             };
6160         },
6161         
6162         /**
6163      * Appends an event handler to an element (shorthand for addListener)
6164      * @param {String/HTMLElement}   element        The html element or id to assign the
6165      * @param {String}   eventName The type of event to listen for
6166      * @param {Function} handler The method the event invokes
6167      * @param {Object}   scope (optional) The scope in which to execute the handler
6168      * function. The handler function's "this" context.
6169      * @param {Object}   options (optional) An object containing handler configuration
6170      * properties. This may contain any of the following properties:<ul>
6171      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6172      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6173      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6174      * <li>preventDefault {Boolean} True to prevent the default action</li>
6175      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6176      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6177      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6178      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6179      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6180      * by the specified number of milliseconds. If the event fires again within that time, the original
6181      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6182      * </ul><br>
6183      * <p>
6184      * <b>Combining Options</b><br>
6185      * Using the options argument, it is possible to combine different types of listeners:<br>
6186      * <br>
6187      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6188      * Code:<pre><code>
6189 el.on('click', this.onClick, this, {
6190     single: true,
6191     delay: 100,
6192     stopEvent : true,
6193     forumId: 4
6194 });</code></pre>
6195      * <p>
6196      * <b>Attaching multiple handlers in 1 call</b><br>
6197       * The method also allows for a single argument to be passed which is a config object containing properties
6198      * which specify multiple handlers.
6199      * <p>
6200      * Code:<pre><code>
6201 el.on({
6202     'click' : {
6203         fn: this.onClick
6204         scope: this,
6205         delay: 100
6206     },
6207     'mouseover' : {
6208         fn: this.onMouseOver
6209         scope: this
6210     },
6211     'mouseout' : {
6212         fn: this.onMouseOut
6213         scope: this
6214     }
6215 });</code></pre>
6216      * <p>
6217      * Or a shorthand syntax:<br>
6218      * Code:<pre><code>
6219 el.on({
6220     'click' : this.onClick,
6221     'mouseover' : this.onMouseOver,
6222     'mouseout' : this.onMouseOut
6223     scope: this
6224 });</code></pre>
6225      */
6226         addListener : function(element, eventName, fn, scope, options){
6227             if(typeof eventName == "object"){
6228                 var o = eventName;
6229                 for(var e in o){
6230                     if(propRe.test(e)){
6231                         continue;
6232                     }
6233                     if(typeof o[e] == "function"){
6234                         // shared options
6235                         listen(element, e, o, o[e], o.scope);
6236                     }else{
6237                         // individual options
6238                         listen(element, e, o[e]);
6239                     }
6240                 }
6241                 return;
6242             }
6243             return listen(element, eventName, options, fn, scope);
6244         },
6245         
6246         /**
6247          * Removes an event handler
6248          *
6249          * @param {String/HTMLElement}   element        The id or html element to remove the 
6250          *                             event from
6251          * @param {String}   eventName     The type of event
6252          * @param {Function} fn
6253          * @return {Boolean} True if a listener was actually removed
6254          */
6255         removeListener : function(element, eventName, fn){
6256             return stopListening(element, eventName, fn);
6257         },
6258         
6259         /**
6260          * Fires when the document is ready (before onload and before images are loaded). Can be 
6261          * accessed shorthanded Roo.onReady().
6262          * @param {Function} fn        The method the event invokes
6263          * @param {Object}   scope    An  object that becomes the scope of the handler
6264          * @param {boolean}  options
6265          */
6266         onDocumentReady : function(fn, scope, options){
6267             if(docReadyState){ // if it already fired
6268                 docReadyEvent.addListener(fn, scope, options);
6269                 docReadyEvent.fire();
6270                 docReadyEvent.clearListeners();
6271                 return;
6272             }
6273             if(!docReadyEvent){
6274                 initDocReady();
6275             }
6276             docReadyEvent.addListener(fn, scope, options);
6277         },
6278         
6279         /**
6280          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6281          * @param {Function} fn        The method the event invokes
6282          * @param {Object}   scope    An object that becomes the scope of the handler
6283          * @param {boolean}  options
6284          */
6285         onWindowResize : function(fn, scope, options){
6286             if(!resizeEvent){
6287                 resizeEvent = new Roo.util.Event();
6288                 resizeTask = new Roo.util.DelayedTask(function(){
6289                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6290                 });
6291                 E.on(window, "resize", function(){
6292                     if(Roo.isIE){
6293                         resizeTask.delay(50);
6294                     }else{
6295                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6296                     }
6297                 });
6298             }
6299             resizeEvent.addListener(fn, scope, options);
6300         },
6301
6302         /**
6303          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6304          * @param {Function} fn        The method the event invokes
6305          * @param {Object}   scope    An object that becomes the scope of the handler
6306          * @param {boolean}  options
6307          */
6308         onTextResize : function(fn, scope, options){
6309             if(!textEvent){
6310                 textEvent = new Roo.util.Event();
6311                 var textEl = new Roo.Element(document.createElement('div'));
6312                 textEl.dom.className = 'x-text-resize';
6313                 textEl.dom.innerHTML = 'X';
6314                 textEl.appendTo(document.body);
6315                 textSize = textEl.dom.offsetHeight;
6316                 setInterval(function(){
6317                     if(textEl.dom.offsetHeight != textSize){
6318                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6319                     }
6320                 }, this.textResizeInterval);
6321             }
6322             textEvent.addListener(fn, scope, options);
6323         },
6324
6325         /**
6326          * Removes the passed window resize listener.
6327          * @param {Function} fn        The method the event invokes
6328          * @param {Object}   scope    The scope of handler
6329          */
6330         removeResizeListener : function(fn, scope){
6331             if(resizeEvent){
6332                 resizeEvent.removeListener(fn, scope);
6333             }
6334         },
6335
6336         // private
6337         fireResize : function(){
6338             if(resizeEvent){
6339                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6340             }   
6341         },
6342         /**
6343          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6344          */
6345         ieDeferSrc : false,
6346         /**
6347          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6348          */
6349         textResizeInterval : 50
6350     };
6351     
6352     /**
6353      * Fix for doc tools
6354      * @scopeAlias pub=Roo.EventManager
6355      */
6356     
6357      /**
6358      * Appends an event handler to an element (shorthand for addListener)
6359      * @param {String/HTMLElement}   element        The html element or id to assign the
6360      * @param {String}   eventName The type of event to listen for
6361      * @param {Function} handler The method the event invokes
6362      * @param {Object}   scope (optional) The scope in which to execute the handler
6363      * function. The handler function's "this" context.
6364      * @param {Object}   options (optional) An object containing handler configuration
6365      * properties. This may contain any of the following properties:<ul>
6366      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6367      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6368      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6369      * <li>preventDefault {Boolean} True to prevent the default action</li>
6370      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6371      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6372      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6373      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6374      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6375      * by the specified number of milliseconds. If the event fires again within that time, the original
6376      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6377      * </ul><br>
6378      * <p>
6379      * <b>Combining Options</b><br>
6380      * Using the options argument, it is possible to combine different types of listeners:<br>
6381      * <br>
6382      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6383      * Code:<pre><code>
6384 el.on('click', this.onClick, this, {
6385     single: true,
6386     delay: 100,
6387     stopEvent : true,
6388     forumId: 4
6389 });</code></pre>
6390      * <p>
6391      * <b>Attaching multiple handlers in 1 call</b><br>
6392       * The method also allows for a single argument to be passed which is a config object containing properties
6393      * which specify multiple handlers.
6394      * <p>
6395      * Code:<pre><code>
6396 el.on({
6397     'click' : {
6398         fn: this.onClick
6399         scope: this,
6400         delay: 100
6401     },
6402     'mouseover' : {
6403         fn: this.onMouseOver
6404         scope: this
6405     },
6406     'mouseout' : {
6407         fn: this.onMouseOut
6408         scope: this
6409     }
6410 });</code></pre>
6411      * <p>
6412      * Or a shorthand syntax:<br>
6413      * Code:<pre><code>
6414 el.on({
6415     'click' : this.onClick,
6416     'mouseover' : this.onMouseOver,
6417     'mouseout' : this.onMouseOut
6418     scope: this
6419 });</code></pre>
6420      */
6421     pub.on = pub.addListener;
6422     pub.un = pub.removeListener;
6423
6424     pub.stoppedMouseDownEvent = new Roo.util.Event();
6425     return pub;
6426 }();
6427 /**
6428   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6429   * @param {Function} fn        The method the event invokes
6430   * @param {Object}   scope    An  object that becomes the scope of the handler
6431   * @param {boolean}  override If true, the obj passed in becomes
6432   *                             the execution scope of the listener
6433   * @member Roo
6434   * @method onReady
6435  */
6436 Roo.onReady = Roo.EventManager.onDocumentReady;
6437
6438 Roo.onReady(function(){
6439     var bd = Roo.get(document.body);
6440     if(!bd){ return; }
6441
6442     var cls = [
6443             Roo.isIE ? "roo-ie"
6444             : Roo.isGecko ? "roo-gecko"
6445             : Roo.isOpera ? "roo-opera"
6446             : Roo.isSafari ? "roo-safari" : ""];
6447
6448     if(Roo.isMac){
6449         cls.push("roo-mac");
6450     }
6451     if(Roo.isLinux){
6452         cls.push("roo-linux");
6453     }
6454     if(Roo.isBorderBox){
6455         cls.push('roo-border-box');
6456     }
6457     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6458         var p = bd.dom.parentNode;
6459         if(p){
6460             p.className += ' roo-strict';
6461         }
6462     }
6463     bd.addClass(cls.join(' '));
6464 });
6465
6466 /**
6467  * @class Roo.EventObject
6468  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6469  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6470  * Example:
6471  * <pre><code>
6472  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6473     e.preventDefault();
6474     var target = e.getTarget();
6475     ...
6476  }
6477  var myDiv = Roo.get("myDiv");
6478  myDiv.on("click", handleClick);
6479  //or
6480  Roo.EventManager.on("myDiv", 'click', handleClick);
6481  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6482  </code></pre>
6483  * @singleton
6484  */
6485 Roo.EventObject = function(){
6486     
6487     var E = Roo.lib.Event;
6488     
6489     // safari keypress events for special keys return bad keycodes
6490     var safariKeys = {
6491         63234 : 37, // left
6492         63235 : 39, // right
6493         63232 : 38, // up
6494         63233 : 40, // down
6495         63276 : 33, // page up
6496         63277 : 34, // page down
6497         63272 : 46, // delete
6498         63273 : 36, // home
6499         63275 : 35  // end
6500     };
6501
6502     // normalize button clicks
6503     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6504                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6505
6506     Roo.EventObjectImpl = function(e){
6507         if(e){
6508             this.setEvent(e.browserEvent || e);
6509         }
6510     };
6511     Roo.EventObjectImpl.prototype = {
6512         /**
6513          * Used to fix doc tools.
6514          * @scope Roo.EventObject.prototype
6515          */
6516             
6517
6518         
6519         
6520         /** The normal browser event */
6521         browserEvent : null,
6522         /** The button pressed in a mouse event */
6523         button : -1,
6524         /** True if the shift key was down during the event */
6525         shiftKey : false,
6526         /** True if the control key was down during the event */
6527         ctrlKey : false,
6528         /** True if the alt key was down during the event */
6529         altKey : false,
6530
6531         /** Key constant 
6532         * @type Number */
6533         BACKSPACE : 8,
6534         /** Key constant 
6535         * @type Number */
6536         TAB : 9,
6537         /** Key constant 
6538         * @type Number */
6539         RETURN : 13,
6540         /** Key constant 
6541         * @type Number */
6542         ENTER : 13,
6543         /** Key constant 
6544         * @type Number */
6545         SHIFT : 16,
6546         /** Key constant 
6547         * @type Number */
6548         CONTROL : 17,
6549         /** Key constant 
6550         * @type Number */
6551         ESC : 27,
6552         /** Key constant 
6553         * @type Number */
6554         SPACE : 32,
6555         /** Key constant 
6556         * @type Number */
6557         PAGEUP : 33,
6558         /** Key constant 
6559         * @type Number */
6560         PAGEDOWN : 34,
6561         /** Key constant 
6562         * @type Number */
6563         END : 35,
6564         /** Key constant 
6565         * @type Number */
6566         HOME : 36,
6567         /** Key constant 
6568         * @type Number */
6569         LEFT : 37,
6570         /** Key constant 
6571         * @type Number */
6572         UP : 38,
6573         /** Key constant 
6574         * @type Number */
6575         RIGHT : 39,
6576         /** Key constant 
6577         * @type Number */
6578         DOWN : 40,
6579         /** Key constant 
6580         * @type Number */
6581         DELETE : 46,
6582         /** Key constant 
6583         * @type Number */
6584         F5 : 116,
6585
6586            /** @private */
6587         setEvent : function(e){
6588             if(e == this || (e && e.browserEvent)){ // already wrapped
6589                 return e;
6590             }
6591             this.browserEvent = e;
6592             if(e){
6593                 // normalize buttons
6594                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6595                 if(e.type == 'click' && this.button == -1){
6596                     this.button = 0;
6597                 }
6598                 this.type = e.type;
6599                 this.shiftKey = e.shiftKey;
6600                 // mac metaKey behaves like ctrlKey
6601                 this.ctrlKey = e.ctrlKey || e.metaKey;
6602                 this.altKey = e.altKey;
6603                 // in getKey these will be normalized for the mac
6604                 this.keyCode = e.keyCode;
6605                 // keyup warnings on firefox.
6606                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6607                 // cache the target for the delayed and or buffered events
6608                 this.target = E.getTarget(e);
6609                 // same for XY
6610                 this.xy = E.getXY(e);
6611             }else{
6612                 this.button = -1;
6613                 this.shiftKey = false;
6614                 this.ctrlKey = false;
6615                 this.altKey = false;
6616                 this.keyCode = 0;
6617                 this.charCode =0;
6618                 this.target = null;
6619                 this.xy = [0, 0];
6620             }
6621             return this;
6622         },
6623
6624         /**
6625          * Stop the event (preventDefault and stopPropagation)
6626          */
6627         stopEvent : function(){
6628             if(this.browserEvent){
6629                 if(this.browserEvent.type == 'mousedown'){
6630                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6631                 }
6632                 E.stopEvent(this.browserEvent);
6633             }
6634         },
6635
6636         /**
6637          * Prevents the browsers default handling of the event.
6638          */
6639         preventDefault : function(){
6640             if(this.browserEvent){
6641                 E.preventDefault(this.browserEvent);
6642             }
6643         },
6644
6645         /** @private */
6646         isNavKeyPress : function(){
6647             var k = this.keyCode;
6648             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6649             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6650         },
6651
6652         isSpecialKey : function(){
6653             var k = this.keyCode;
6654             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6655             (k == 16) || (k == 17) ||
6656             (k >= 18 && k <= 20) ||
6657             (k >= 33 && k <= 35) ||
6658             (k >= 36 && k <= 39) ||
6659             (k >= 44 && k <= 45);
6660         },
6661         /**
6662          * Cancels bubbling of the event.
6663          */
6664         stopPropagation : function(){
6665             if(this.browserEvent){
6666                 if(this.type == 'mousedown'){
6667                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6668                 }
6669                 E.stopPropagation(this.browserEvent);
6670             }
6671         },
6672
6673         /**
6674          * Gets the key code for the event.
6675          * @return {Number}
6676          */
6677         getCharCode : function(){
6678             return this.charCode || this.keyCode;
6679         },
6680
6681         /**
6682          * Returns a normalized keyCode for the event.
6683          * @return {Number} The key code
6684          */
6685         getKey : function(){
6686             var k = this.keyCode || this.charCode;
6687             return Roo.isSafari ? (safariKeys[k] || k) : k;
6688         },
6689
6690         /**
6691          * Gets the x coordinate of the event.
6692          * @return {Number}
6693          */
6694         getPageX : function(){
6695             return this.xy[0];
6696         },
6697
6698         /**
6699          * Gets the y coordinate of the event.
6700          * @return {Number}
6701          */
6702         getPageY : function(){
6703             return this.xy[1];
6704         },
6705
6706         /**
6707          * Gets the time of the event.
6708          * @return {Number}
6709          */
6710         getTime : function(){
6711             if(this.browserEvent){
6712                 return E.getTime(this.browserEvent);
6713             }
6714             return null;
6715         },
6716
6717         /**
6718          * Gets the page coordinates of the event.
6719          * @return {Array} The xy values like [x, y]
6720          */
6721         getXY : function(){
6722             return this.xy;
6723         },
6724
6725         /**
6726          * Gets the target for the event.
6727          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6728          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6729                 search as a number or element (defaults to 10 || document.body)
6730          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6731          * @return {HTMLelement}
6732          */
6733         getTarget : function(selector, maxDepth, returnEl){
6734             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6735         },
6736         /**
6737          * Gets the related target.
6738          * @return {HTMLElement}
6739          */
6740         getRelatedTarget : function(){
6741             if(this.browserEvent){
6742                 return E.getRelatedTarget(this.browserEvent);
6743             }
6744             return null;
6745         },
6746
6747         /**
6748          * Normalizes mouse wheel delta across browsers
6749          * @return {Number} The delta
6750          */
6751         getWheelDelta : function(){
6752             var e = this.browserEvent;
6753             var delta = 0;
6754             if(e.wheelDelta){ /* IE/Opera. */
6755                 delta = e.wheelDelta/120;
6756             }else if(e.detail){ /* Mozilla case. */
6757                 delta = -e.detail/3;
6758             }
6759             return delta;
6760         },
6761
6762         /**
6763          * Returns true if the control, meta, shift or alt key was pressed during this event.
6764          * @return {Boolean}
6765          */
6766         hasModifier : function(){
6767             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6768         },
6769
6770         /**
6771          * Returns true if the target of this event equals el or is a child of el
6772          * @param {String/HTMLElement/Element} el
6773          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6774          * @return {Boolean}
6775          */
6776         within : function(el, related){
6777             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6778             return t && Roo.fly(el).contains(t);
6779         },
6780
6781         getPoint : function(){
6782             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6783         }
6784     };
6785
6786     return new Roo.EventObjectImpl();
6787 }();
6788             
6789     /*
6790  * Based on:
6791  * Ext JS Library 1.1.1
6792  * Copyright(c) 2006-2007, Ext JS, LLC.
6793  *
6794  * Originally Released Under LGPL - original licence link has changed is not relivant.
6795  *
6796  * Fork - LGPL
6797  * <script type="text/javascript">
6798  */
6799
6800  
6801 // was in Composite Element!??!?!
6802  
6803 (function(){
6804     var D = Roo.lib.Dom;
6805     var E = Roo.lib.Event;
6806     var A = Roo.lib.Anim;
6807
6808     // local style camelizing for speed
6809     var propCache = {};
6810     var camelRe = /(-[a-z])/gi;
6811     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6812     var view = document.defaultView;
6813
6814 /**
6815  * @class Roo.Element
6816  * Represents an Element in the DOM.<br><br>
6817  * Usage:<br>
6818 <pre><code>
6819 var el = Roo.get("my-div");
6820
6821 // or with getEl
6822 var el = getEl("my-div");
6823
6824 // or with a DOM element
6825 var el = Roo.get(myDivElement);
6826 </code></pre>
6827  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6828  * each call instead of constructing a new one.<br><br>
6829  * <b>Animations</b><br />
6830  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6831  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6832 <pre>
6833 Option    Default   Description
6834 --------- --------  ---------------------------------------------
6835 duration  .35       The duration of the animation in seconds
6836 easing    easeOut   The YUI easing method
6837 callback  none      A function to execute when the anim completes
6838 scope     this      The scope (this) of the callback function
6839 </pre>
6840 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6841 * manipulate the animation. Here's an example:
6842 <pre><code>
6843 var el = Roo.get("my-div");
6844
6845 // no animation
6846 el.setWidth(100);
6847
6848 // default animation
6849 el.setWidth(100, true);
6850
6851 // animation with some options set
6852 el.setWidth(100, {
6853     duration: 1,
6854     callback: this.foo,
6855     scope: this
6856 });
6857
6858 // using the "anim" property to get the Anim object
6859 var opt = {
6860     duration: 1,
6861     callback: this.foo,
6862     scope: this
6863 };
6864 el.setWidth(100, opt);
6865 ...
6866 if(opt.anim.isAnimated()){
6867     opt.anim.stop();
6868 }
6869 </code></pre>
6870 * <b> Composite (Collections of) Elements</b><br />
6871  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6872  * @constructor Create a new Element directly.
6873  * @param {String/HTMLElement} element
6874  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6875  */
6876     Roo.Element = function(element, forceNew){
6877         var dom = typeof element == "string" ?
6878                 document.getElementById(element) : element;
6879         if(!dom){ // invalid id/element
6880             return null;
6881         }
6882         var id = dom.id;
6883         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6884             return Roo.Element.cache[id];
6885         }
6886
6887         /**
6888          * The DOM element
6889          * @type HTMLElement
6890          */
6891         this.dom = dom;
6892
6893         /**
6894          * The DOM element ID
6895          * @type String
6896          */
6897         this.id = id || Roo.id(dom);
6898     };
6899
6900     var El = Roo.Element;
6901
6902     El.prototype = {
6903         /**
6904          * The element's default display mode  (defaults to "")
6905          * @type String
6906          */
6907         originalDisplay : "",
6908
6909         visibilityMode : 1,
6910         /**
6911          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6912          * @type String
6913          */
6914         defaultUnit : "px",
6915         /**
6916          * Sets the element's visibility mode. When setVisible() is called it
6917          * will use this to determine whether to set the visibility or the display property.
6918          * @param visMode Element.VISIBILITY or Element.DISPLAY
6919          * @return {Roo.Element} this
6920          */
6921         setVisibilityMode : function(visMode){
6922             this.visibilityMode = visMode;
6923             return this;
6924         },
6925         /**
6926          * Convenience method for setVisibilityMode(Element.DISPLAY)
6927          * @param {String} display (optional) What to set display to when visible
6928          * @return {Roo.Element} this
6929          */
6930         enableDisplayMode : function(display){
6931             this.setVisibilityMode(El.DISPLAY);
6932             if(typeof display != "undefined") this.originalDisplay = display;
6933             return this;
6934         },
6935
6936         /**
6937          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6938          * @param {String} selector The simple selector to test
6939          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6940                 search as a number or element (defaults to 10 || document.body)
6941          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6942          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6943          */
6944         findParent : function(simpleSelector, maxDepth, returnEl){
6945             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6946             maxDepth = maxDepth || 50;
6947             if(typeof maxDepth != "number"){
6948                 stopEl = Roo.getDom(maxDepth);
6949                 maxDepth = 10;
6950             }
6951             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6952                 if(dq.is(p, simpleSelector)){
6953                     return returnEl ? Roo.get(p) : p;
6954                 }
6955                 depth++;
6956                 p = p.parentNode;
6957             }
6958             return null;
6959         },
6960
6961
6962         /**
6963          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6964          * @param {String} selector The simple selector to test
6965          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6966                 search as a number or element (defaults to 10 || document.body)
6967          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6968          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6969          */
6970         findParentNode : function(simpleSelector, maxDepth, returnEl){
6971             var p = Roo.fly(this.dom.parentNode, '_internal');
6972             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6973         },
6974
6975         /**
6976          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6977          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6978          * @param {String} selector The simple selector to test
6979          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6980                 search as a number or element (defaults to 10 || document.body)
6981          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6982          */
6983         up : function(simpleSelector, maxDepth){
6984             return this.findParentNode(simpleSelector, maxDepth, true);
6985         },
6986
6987
6988
6989         /**
6990          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6991          * @param {String} selector The simple selector to test
6992          * @return {Boolean} True if this element matches the selector, else false
6993          */
6994         is : function(simpleSelector){
6995             return Roo.DomQuery.is(this.dom, simpleSelector);
6996         },
6997
6998         /**
6999          * Perform animation on this element.
7000          * @param {Object} args The YUI animation control args
7001          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7002          * @param {Function} onComplete (optional) Function to call when animation completes
7003          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7004          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7005          * @return {Roo.Element} this
7006          */
7007         animate : function(args, duration, onComplete, easing, animType){
7008             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7009             return this;
7010         },
7011
7012         /*
7013          * @private Internal animation call
7014          */
7015         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7016             animType = animType || 'run';
7017             opt = opt || {};
7018             var anim = Roo.lib.Anim[animType](
7019                 this.dom, args,
7020                 (opt.duration || defaultDur) || .35,
7021                 (opt.easing || defaultEase) || 'easeOut',
7022                 function(){
7023                     Roo.callback(cb, this);
7024                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7025                 },
7026                 this
7027             );
7028             opt.anim = anim;
7029             return anim;
7030         },
7031
7032         // private legacy anim prep
7033         preanim : function(a, i){
7034             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7035         },
7036
7037         /**
7038          * Removes worthless text nodes
7039          * @param {Boolean} forceReclean (optional) By default the element
7040          * keeps track if it has been cleaned already so
7041          * you can call this over and over. However, if you update the element and
7042          * need to force a reclean, you can pass true.
7043          */
7044         clean : function(forceReclean){
7045             if(this.isCleaned && forceReclean !== true){
7046                 return this;
7047             }
7048             var ns = /\S/;
7049             var d = this.dom, n = d.firstChild, ni = -1;
7050             while(n){
7051                 var nx = n.nextSibling;
7052                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7053                     d.removeChild(n);
7054                 }else{
7055                     n.nodeIndex = ++ni;
7056                 }
7057                 n = nx;
7058             }
7059             this.isCleaned = true;
7060             return this;
7061         },
7062
7063         // private
7064         calcOffsetsTo : function(el){
7065             el = Roo.get(el);
7066             var d = el.dom;
7067             var restorePos = false;
7068             if(el.getStyle('position') == 'static'){
7069                 el.position('relative');
7070                 restorePos = true;
7071             }
7072             var x = 0, y =0;
7073             var op = this.dom;
7074             while(op && op != d && op.tagName != 'HTML'){
7075                 x+= op.offsetLeft;
7076                 y+= op.offsetTop;
7077                 op = op.offsetParent;
7078             }
7079             if(restorePos){
7080                 el.position('static');
7081             }
7082             return [x, y];
7083         },
7084
7085         /**
7086          * Scrolls this element into view within the passed container.
7087          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7088          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7089          * @return {Roo.Element} this
7090          */
7091         scrollIntoView : function(container, hscroll){
7092             var c = Roo.getDom(container) || document.body;
7093             var el = this.dom;
7094
7095             var o = this.calcOffsetsTo(c),
7096                 l = o[0],
7097                 t = o[1],
7098                 b = t+el.offsetHeight,
7099                 r = l+el.offsetWidth;
7100
7101             var ch = c.clientHeight;
7102             var ct = parseInt(c.scrollTop, 10);
7103             var cl = parseInt(c.scrollLeft, 10);
7104             var cb = ct + ch;
7105             var cr = cl + c.clientWidth;
7106
7107             if(t < ct){
7108                 c.scrollTop = t;
7109             }else if(b > cb){
7110                 c.scrollTop = b-ch;
7111             }
7112
7113             if(hscroll !== false){
7114                 if(l < cl){
7115                     c.scrollLeft = l;
7116                 }else if(r > cr){
7117                     c.scrollLeft = r-c.clientWidth;
7118                 }
7119             }
7120             return this;
7121         },
7122
7123         // private
7124         scrollChildIntoView : function(child, hscroll){
7125             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7126         },
7127
7128         /**
7129          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7130          * the new height may not be available immediately.
7131          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7132          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7133          * @param {Function} onComplete (optional) Function to call when animation completes
7134          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7135          * @return {Roo.Element} this
7136          */
7137         autoHeight : function(animate, duration, onComplete, easing){
7138             var oldHeight = this.getHeight();
7139             this.clip();
7140             this.setHeight(1); // force clipping
7141             setTimeout(function(){
7142                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7143                 if(!animate){
7144                     this.setHeight(height);
7145                     this.unclip();
7146                     if(typeof onComplete == "function"){
7147                         onComplete();
7148                     }
7149                 }else{
7150                     this.setHeight(oldHeight); // restore original height
7151                     this.setHeight(height, animate, duration, function(){
7152                         this.unclip();
7153                         if(typeof onComplete == "function") onComplete();
7154                     }.createDelegate(this), easing);
7155                 }
7156             }.createDelegate(this), 0);
7157             return this;
7158         },
7159
7160         /**
7161          * Returns true if this element is an ancestor of the passed element
7162          * @param {HTMLElement/String} el The element to check
7163          * @return {Boolean} True if this element is an ancestor of el, else false
7164          */
7165         contains : function(el){
7166             if(!el){return false;}
7167             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7168         },
7169
7170         /**
7171          * Checks whether the element is currently visible using both visibility and display properties.
7172          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7173          * @return {Boolean} True if the element is currently visible, else false
7174          */
7175         isVisible : function(deep) {
7176             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7177             if(deep !== true || !vis){
7178                 return vis;
7179             }
7180             var p = this.dom.parentNode;
7181             while(p && p.tagName.toLowerCase() != "body"){
7182                 if(!Roo.fly(p, '_isVisible').isVisible()){
7183                     return false;
7184                 }
7185                 p = p.parentNode;
7186             }
7187             return true;
7188         },
7189
7190         /**
7191          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7192          * @param {String} selector The CSS selector
7193          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7194          * @return {CompositeElement/CompositeElementLite} The composite element
7195          */
7196         select : function(selector, unique){
7197             return El.select(selector, unique, this.dom);
7198         },
7199
7200         /**
7201          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7202          * @param {String} selector The CSS selector
7203          * @return {Array} An array of the matched nodes
7204          */
7205         query : function(selector, unique){
7206             return Roo.DomQuery.select(selector, this.dom);
7207         },
7208
7209         /**
7210          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7211          * @param {String} selector The CSS selector
7212          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7213          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7214          */
7215         child : function(selector, returnDom){
7216             var n = Roo.DomQuery.selectNode(selector, this.dom);
7217             return returnDom ? n : Roo.get(n);
7218         },
7219
7220         /**
7221          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7222          * @param {String} selector The CSS selector
7223          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7224          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7225          */
7226         down : function(selector, returnDom){
7227             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7228             return returnDom ? n : Roo.get(n);
7229         },
7230
7231         /**
7232          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7233          * @param {String} group The group the DD object is member of
7234          * @param {Object} config The DD config object
7235          * @param {Object} overrides An object containing methods to override/implement on the DD object
7236          * @return {Roo.dd.DD} The DD object
7237          */
7238         initDD : function(group, config, overrides){
7239             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7240             return Roo.apply(dd, overrides);
7241         },
7242
7243         /**
7244          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7245          * @param {String} group The group the DDProxy object is member of
7246          * @param {Object} config The DDProxy config object
7247          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7248          * @return {Roo.dd.DDProxy} The DDProxy object
7249          */
7250         initDDProxy : function(group, config, overrides){
7251             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7252             return Roo.apply(dd, overrides);
7253         },
7254
7255         /**
7256          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7257          * @param {String} group The group the DDTarget object is member of
7258          * @param {Object} config The DDTarget config object
7259          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7260          * @return {Roo.dd.DDTarget} The DDTarget object
7261          */
7262         initDDTarget : function(group, config, overrides){
7263             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7264             return Roo.apply(dd, overrides);
7265         },
7266
7267         /**
7268          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7269          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7270          * @param {Boolean} visible Whether the element is visible
7271          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7272          * @return {Roo.Element} this
7273          */
7274          setVisible : function(visible, animate){
7275             if(!animate || !A){
7276                 if(this.visibilityMode == El.DISPLAY){
7277                     this.setDisplayed(visible);
7278                 }else{
7279                     this.fixDisplay();
7280                     this.dom.style.visibility = visible ? "visible" : "hidden";
7281                 }
7282             }else{
7283                 // closure for composites
7284                 var dom = this.dom;
7285                 var visMode = this.visibilityMode;
7286                 if(visible){
7287                     this.setOpacity(.01);
7288                     this.setVisible(true);
7289                 }
7290                 this.anim({opacity: { to: (visible?1:0) }},
7291                       this.preanim(arguments, 1),
7292                       null, .35, 'easeIn', function(){
7293                          if(!visible){
7294                              if(visMode == El.DISPLAY){
7295                                  dom.style.display = "none";
7296                              }else{
7297                                  dom.style.visibility = "hidden";
7298                              }
7299                              Roo.get(dom).setOpacity(1);
7300                          }
7301                      });
7302             }
7303             return this;
7304         },
7305
7306         /**
7307          * Returns true if display is not "none"
7308          * @return {Boolean}
7309          */
7310         isDisplayed : function() {
7311             return this.getStyle("display") != "none";
7312         },
7313
7314         /**
7315          * Toggles the element's visibility or display, depending on visibility mode.
7316          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7317          * @return {Roo.Element} this
7318          */
7319         toggle : function(animate){
7320             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7321             return this;
7322         },
7323
7324         /**
7325          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7326          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7327          * @return {Roo.Element} this
7328          */
7329         setDisplayed : function(value) {
7330             if(typeof value == "boolean"){
7331                value = value ? this.originalDisplay : "none";
7332             }
7333             this.setStyle("display", value);
7334             return this;
7335         },
7336
7337         /**
7338          * Tries to focus the element. Any exceptions are caught and ignored.
7339          * @return {Roo.Element} this
7340          */
7341         focus : function() {
7342             try{
7343                 this.dom.focus();
7344             }catch(e){}
7345             return this;
7346         },
7347
7348         /**
7349          * Tries to blur the element. Any exceptions are caught and ignored.
7350          * @return {Roo.Element} this
7351          */
7352         blur : function() {
7353             try{
7354                 this.dom.blur();
7355             }catch(e){}
7356             return this;
7357         },
7358
7359         /**
7360          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7361          * @param {String/Array} className The CSS class to add, or an array of classes
7362          * @return {Roo.Element} this
7363          */
7364         addClass : function(className){
7365             if(className instanceof Array){
7366                 for(var i = 0, len = className.length; i < len; i++) {
7367                     this.addClass(className[i]);
7368                 }
7369             }else{
7370                 if(className && !this.hasClass(className)){
7371                     this.dom.className = this.dom.className + " " + className;
7372                 }
7373             }
7374             return this;
7375         },
7376
7377         /**
7378          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7379          * @param {String/Array} className The CSS class to add, or an array of classes
7380          * @return {Roo.Element} this
7381          */
7382         radioClass : function(className){
7383             var siblings = this.dom.parentNode.childNodes;
7384             for(var i = 0; i < siblings.length; i++) {
7385                 var s = siblings[i];
7386                 if(s.nodeType == 1){
7387                     Roo.get(s).removeClass(className);
7388                 }
7389             }
7390             this.addClass(className);
7391             return this;
7392         },
7393
7394         /**
7395          * Removes one or more CSS classes from the element.
7396          * @param {String/Array} className The CSS class to remove, or an array of classes
7397          * @return {Roo.Element} this
7398          */
7399         removeClass : function(className){
7400             if(!className || !this.dom.className){
7401                 return this;
7402             }
7403             if(className instanceof Array){
7404                 for(var i = 0, len = className.length; i < len; i++) {
7405                     this.removeClass(className[i]);
7406                 }
7407             }else{
7408                 if(this.hasClass(className)){
7409                     var re = this.classReCache[className];
7410                     if (!re) {
7411                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7412                        this.classReCache[className] = re;
7413                     }
7414                     this.dom.className =
7415                         this.dom.className.replace(re, " ");
7416                 }
7417             }
7418             return this;
7419         },
7420
7421         // private
7422         classReCache: {},
7423
7424         /**
7425          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7426          * @param {String} className The CSS class to toggle
7427          * @return {Roo.Element} this
7428          */
7429         toggleClass : function(className){
7430             if(this.hasClass(className)){
7431                 this.removeClass(className);
7432             }else{
7433                 this.addClass(className);
7434             }
7435             return this;
7436         },
7437
7438         /**
7439          * Checks if the specified CSS class exists on this element's DOM node.
7440          * @param {String} className The CSS class to check for
7441          * @return {Boolean} True if the class exists, else false
7442          */
7443         hasClass : function(className){
7444             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7445         },
7446
7447         /**
7448          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7449          * @param {String} oldClassName The CSS class to replace
7450          * @param {String} newClassName The replacement CSS class
7451          * @return {Roo.Element} this
7452          */
7453         replaceClass : function(oldClassName, newClassName){
7454             this.removeClass(oldClassName);
7455             this.addClass(newClassName);
7456             return this;
7457         },
7458
7459         /**
7460          * Returns an object with properties matching the styles requested.
7461          * For example, el.getStyles('color', 'font-size', 'width') might return
7462          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7463          * @param {String} style1 A style name
7464          * @param {String} style2 A style name
7465          * @param {String} etc.
7466          * @return {Object} The style object
7467          */
7468         getStyles : function(){
7469             var a = arguments, len = a.length, r = {};
7470             for(var i = 0; i < len; i++){
7471                 r[a[i]] = this.getStyle(a[i]);
7472             }
7473             return r;
7474         },
7475
7476         /**
7477          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7478          * @param {String} property The style property whose value is returned.
7479          * @return {String} The current value of the style property for this element.
7480          */
7481         getStyle : function(){
7482             return view && view.getComputedStyle ?
7483                 function(prop){
7484                     var el = this.dom, v, cs, camel;
7485                     if(prop == 'float'){
7486                         prop = "cssFloat";
7487                     }
7488                     if(el.style && (v = el.style[prop])){
7489                         return v;
7490                     }
7491                     if(cs = view.getComputedStyle(el, "")){
7492                         if(!(camel = propCache[prop])){
7493                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7494                         }
7495                         return cs[camel];
7496                     }
7497                     return null;
7498                 } :
7499                 function(prop){
7500                     var el = this.dom, v, cs, camel;
7501                     if(prop == 'opacity'){
7502                         if(typeof el.style.filter == 'string'){
7503                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7504                             if(m){
7505                                 var fv = parseFloat(m[1]);
7506                                 if(!isNaN(fv)){
7507                                     return fv ? fv / 100 : 0;
7508                                 }
7509                             }
7510                         }
7511                         return 1;
7512                     }else if(prop == 'float'){
7513                         prop = "styleFloat";
7514                     }
7515                     if(!(camel = propCache[prop])){
7516                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7517                     }
7518                     if(v = el.style[camel]){
7519                         return v;
7520                     }
7521                     if(cs = el.currentStyle){
7522                         return cs[camel];
7523                     }
7524                     return null;
7525                 };
7526         }(),
7527
7528         /**
7529          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7530          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7531          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7532          * @return {Roo.Element} this
7533          */
7534         setStyle : function(prop, value){
7535             if(typeof prop == "string"){
7536                 
7537                 if (prop == 'float') {
7538                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7539                     return this;
7540                 }
7541                 
7542                 var camel;
7543                 if(!(camel = propCache[prop])){
7544                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7545                 }
7546                 
7547                 if(camel == 'opacity') {
7548                     this.setOpacity(value);
7549                 }else{
7550                     this.dom.style[camel] = value;
7551                 }
7552             }else{
7553                 for(var style in prop){
7554                     if(typeof prop[style] != "function"){
7555                        this.setStyle(style, prop[style]);
7556                     }
7557                 }
7558             }
7559             return this;
7560         },
7561
7562         /**
7563          * More flexible version of {@link #setStyle} for setting style properties.
7564          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7565          * a function which returns such a specification.
7566          * @return {Roo.Element} this
7567          */
7568         applyStyles : function(style){
7569             Roo.DomHelper.applyStyles(this.dom, style);
7570             return this;
7571         },
7572
7573         /**
7574           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7575           * @return {Number} The X position of the element
7576           */
7577         getX : function(){
7578             return D.getX(this.dom);
7579         },
7580
7581         /**
7582           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7583           * @return {Number} The Y position of the element
7584           */
7585         getY : function(){
7586             return D.getY(this.dom);
7587         },
7588
7589         /**
7590           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7591           * @return {Array} The XY position of the element
7592           */
7593         getXY : function(){
7594             return D.getXY(this.dom);
7595         },
7596
7597         /**
7598          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7599          * @param {Number} The X position of the element
7600          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7601          * @return {Roo.Element} this
7602          */
7603         setX : function(x, animate){
7604             if(!animate || !A){
7605                 D.setX(this.dom, x);
7606             }else{
7607                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7608             }
7609             return this;
7610         },
7611
7612         /**
7613          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7614          * @param {Number} The Y position of the element
7615          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7616          * @return {Roo.Element} this
7617          */
7618         setY : function(y, animate){
7619             if(!animate || !A){
7620                 D.setY(this.dom, y);
7621             }else{
7622                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7623             }
7624             return this;
7625         },
7626
7627         /**
7628          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7629          * @param {String} left The left CSS property value
7630          * @return {Roo.Element} this
7631          */
7632         setLeft : function(left){
7633             this.setStyle("left", this.addUnits(left));
7634             return this;
7635         },
7636
7637         /**
7638          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7639          * @param {String} top The top CSS property value
7640          * @return {Roo.Element} this
7641          */
7642         setTop : function(top){
7643             this.setStyle("top", this.addUnits(top));
7644             return this;
7645         },
7646
7647         /**
7648          * Sets the element's CSS right style.
7649          * @param {String} right The right CSS property value
7650          * @return {Roo.Element} this
7651          */
7652         setRight : function(right){
7653             this.setStyle("right", this.addUnits(right));
7654             return this;
7655         },
7656
7657         /**
7658          * Sets the element's CSS bottom style.
7659          * @param {String} bottom The bottom CSS property value
7660          * @return {Roo.Element} this
7661          */
7662         setBottom : function(bottom){
7663             this.setStyle("bottom", this.addUnits(bottom));
7664             return this;
7665         },
7666
7667         /**
7668          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7669          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7670          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7671          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7672          * @return {Roo.Element} this
7673          */
7674         setXY : function(pos, animate){
7675             if(!animate || !A){
7676                 D.setXY(this.dom, pos);
7677             }else{
7678                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7679             }
7680             return this;
7681         },
7682
7683         /**
7684          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7685          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7686          * @param {Number} x X value for new position (coordinates are page-based)
7687          * @param {Number} y Y value for new position (coordinates are page-based)
7688          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7689          * @return {Roo.Element} this
7690          */
7691         setLocation : function(x, y, animate){
7692             this.setXY([x, y], this.preanim(arguments, 2));
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7698          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7699          * @param {Number} x X value for new position (coordinates are page-based)
7700          * @param {Number} y Y value for new position (coordinates are page-based)
7701          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7702          * @return {Roo.Element} this
7703          */
7704         moveTo : function(x, y, animate){
7705             this.setXY([x, y], this.preanim(arguments, 2));
7706             return this;
7707         },
7708
7709         /**
7710          * Returns the region of the given element.
7711          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7712          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7713          */
7714         getRegion : function(){
7715             return D.getRegion(this.dom);
7716         },
7717
7718         /**
7719          * Returns the offset height of the element
7720          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7721          * @return {Number} The element's height
7722          */
7723         getHeight : function(contentHeight){
7724             var h = this.dom.offsetHeight || 0;
7725             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7726         },
7727
7728         /**
7729          * Returns the offset width of the element
7730          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7731          * @return {Number} The element's width
7732          */
7733         getWidth : function(contentWidth){
7734             var w = this.dom.offsetWidth || 0;
7735             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7736         },
7737
7738         /**
7739          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7740          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7741          * if a height has not been set using CSS.
7742          * @return {Number}
7743          */
7744         getComputedHeight : function(){
7745             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7746             if(!h){
7747                 h = parseInt(this.getStyle('height'), 10) || 0;
7748                 if(!this.isBorderBox()){
7749                     h += this.getFrameWidth('tb');
7750                 }
7751             }
7752             return h;
7753         },
7754
7755         /**
7756          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7757          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7758          * if a width has not been set using CSS.
7759          * @return {Number}
7760          */
7761         getComputedWidth : function(){
7762             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7763             if(!w){
7764                 w = parseInt(this.getStyle('width'), 10) || 0;
7765                 if(!this.isBorderBox()){
7766                     w += this.getFrameWidth('lr');
7767                 }
7768             }
7769             return w;
7770         },
7771
7772         /**
7773          * Returns the size of the element.
7774          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7775          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7776          */
7777         getSize : function(contentSize){
7778             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7779         },
7780
7781         /**
7782          * Returns the width and height of the viewport.
7783          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7784          */
7785         getViewSize : function(){
7786             var d = this.dom, doc = document, aw = 0, ah = 0;
7787             if(d == doc || d == doc.body){
7788                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7789             }else{
7790                 return {
7791                     width : d.clientWidth,
7792                     height: d.clientHeight
7793                 };
7794             }
7795         },
7796
7797         /**
7798          * Returns the value of the "value" attribute
7799          * @param {Boolean} asNumber true to parse the value as a number
7800          * @return {String/Number}
7801          */
7802         getValue : function(asNumber){
7803             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7804         },
7805
7806         // private
7807         adjustWidth : function(width){
7808             if(typeof width == "number"){
7809                 if(this.autoBoxAdjust && !this.isBorderBox()){
7810                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7811                 }
7812                 if(width < 0){
7813                     width = 0;
7814                 }
7815             }
7816             return width;
7817         },
7818
7819         // private
7820         adjustHeight : function(height){
7821             if(typeof height == "number"){
7822                if(this.autoBoxAdjust && !this.isBorderBox()){
7823                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7824                }
7825                if(height < 0){
7826                    height = 0;
7827                }
7828             }
7829             return height;
7830         },
7831
7832         /**
7833          * Set the width of the element
7834          * @param {Number} width The new width
7835          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7836          * @return {Roo.Element} this
7837          */
7838         setWidth : function(width, animate){
7839             width = this.adjustWidth(width);
7840             if(!animate || !A){
7841                 this.dom.style.width = this.addUnits(width);
7842             }else{
7843                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7844             }
7845             return this;
7846         },
7847
7848         /**
7849          * Set the height of the element
7850          * @param {Number} height The new height
7851          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7852          * @return {Roo.Element} this
7853          */
7854          setHeight : function(height, animate){
7855             height = this.adjustHeight(height);
7856             if(!animate || !A){
7857                 this.dom.style.height = this.addUnits(height);
7858             }else{
7859                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7860             }
7861             return this;
7862         },
7863
7864         /**
7865          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7866          * @param {Number} width The new width
7867          * @param {Number} height The new height
7868          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7869          * @return {Roo.Element} this
7870          */
7871          setSize : function(width, height, animate){
7872             if(typeof width == "object"){ // in case of object from getSize()
7873                 height = width.height; width = width.width;
7874             }
7875             width = this.adjustWidth(width); height = this.adjustHeight(height);
7876             if(!animate || !A){
7877                 this.dom.style.width = this.addUnits(width);
7878                 this.dom.style.height = this.addUnits(height);
7879             }else{
7880                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7881             }
7882             return this;
7883         },
7884
7885         /**
7886          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7887          * @param {Number} x X value for new position (coordinates are page-based)
7888          * @param {Number} y Y value for new position (coordinates are page-based)
7889          * @param {Number} width The new width
7890          * @param {Number} height The new height
7891          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7892          * @return {Roo.Element} this
7893          */
7894         setBounds : function(x, y, width, height, animate){
7895             if(!animate || !A){
7896                 this.setSize(width, height);
7897                 this.setLocation(x, y);
7898             }else{
7899                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7900                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7901                               this.preanim(arguments, 4), 'motion');
7902             }
7903             return this;
7904         },
7905
7906         /**
7907          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7908          * @param {Roo.lib.Region} region The region to fill
7909          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7910          * @return {Roo.Element} this
7911          */
7912         setRegion : function(region, animate){
7913             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7914             return this;
7915         },
7916
7917         /**
7918          * Appends an event handler
7919          *
7920          * @param {String}   eventName     The type of event to append
7921          * @param {Function} fn        The method the event invokes
7922          * @param {Object} scope       (optional) The scope (this object) of the fn
7923          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7924          */
7925         addListener : function(eventName, fn, scope, options){
7926             if (this.dom) {
7927                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7928             }
7929         },
7930
7931         /**
7932          * Removes an event handler from this element
7933          * @param {String} eventName the type of event to remove
7934          * @param {Function} fn the method the event invokes
7935          * @return {Roo.Element} this
7936          */
7937         removeListener : function(eventName, fn){
7938             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7939             return this;
7940         },
7941
7942         /**
7943          * Removes all previous added listeners from this element
7944          * @return {Roo.Element} this
7945          */
7946         removeAllListeners : function(){
7947             E.purgeElement(this.dom);
7948             return this;
7949         },
7950
7951         relayEvent : function(eventName, observable){
7952             this.on(eventName, function(e){
7953                 observable.fireEvent(eventName, e);
7954             });
7955         },
7956
7957         /**
7958          * Set the opacity of the element
7959          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963          setOpacity : function(opacity, animate){
7964             if(!animate || !A){
7965                 var s = this.dom.style;
7966                 if(Roo.isIE){
7967                     s.zoom = 1;
7968                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7969                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7970                 }else{
7971                     s.opacity = opacity;
7972                 }
7973             }else{
7974                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7975             }
7976             return this;
7977         },
7978
7979         /**
7980          * Gets the left X coordinate
7981          * @param {Boolean} local True to get the local css position instead of page coordinate
7982          * @return {Number}
7983          */
7984         getLeft : function(local){
7985             if(!local){
7986                 return this.getX();
7987             }else{
7988                 return parseInt(this.getStyle("left"), 10) || 0;
7989             }
7990         },
7991
7992         /**
7993          * Gets the right X coordinate of the element (element X position + element width)
7994          * @param {Boolean} local True to get the local css position instead of page coordinate
7995          * @return {Number}
7996          */
7997         getRight : function(local){
7998             if(!local){
7999                 return this.getX() + this.getWidth();
8000             }else{
8001                 return (this.getLeft(true) + this.getWidth()) || 0;
8002             }
8003         },
8004
8005         /**
8006          * Gets the top Y coordinate
8007          * @param {Boolean} local True to get the local css position instead of page coordinate
8008          * @return {Number}
8009          */
8010         getTop : function(local) {
8011             if(!local){
8012                 return this.getY();
8013             }else{
8014                 return parseInt(this.getStyle("top"), 10) || 0;
8015             }
8016         },
8017
8018         /**
8019          * Gets the bottom Y coordinate of the element (element Y position + element height)
8020          * @param {Boolean} local True to get the local css position instead of page coordinate
8021          * @return {Number}
8022          */
8023         getBottom : function(local){
8024             if(!local){
8025                 return this.getY() + this.getHeight();
8026             }else{
8027                 return (this.getTop(true) + this.getHeight()) || 0;
8028             }
8029         },
8030
8031         /**
8032         * Initializes positioning on this element. If a desired position is not passed, it will make the
8033         * the element positioned relative IF it is not already positioned.
8034         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8035         * @param {Number} zIndex (optional) The zIndex to apply
8036         * @param {Number} x (optional) Set the page X position
8037         * @param {Number} y (optional) Set the page Y position
8038         */
8039         position : function(pos, zIndex, x, y){
8040             if(!pos){
8041                if(this.getStyle('position') == 'static'){
8042                    this.setStyle('position', 'relative');
8043                }
8044             }else{
8045                 this.setStyle("position", pos);
8046             }
8047             if(zIndex){
8048                 this.setStyle("z-index", zIndex);
8049             }
8050             if(x !== undefined && y !== undefined){
8051                 this.setXY([x, y]);
8052             }else if(x !== undefined){
8053                 this.setX(x);
8054             }else if(y !== undefined){
8055                 this.setY(y);
8056             }
8057         },
8058
8059         /**
8060         * Clear positioning back to the default when the document was loaded
8061         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8062         * @return {Roo.Element} this
8063          */
8064         clearPositioning : function(value){
8065             value = value ||'';
8066             this.setStyle({
8067                 "left": value,
8068                 "right": value,
8069                 "top": value,
8070                 "bottom": value,
8071                 "z-index": "",
8072                 "position" : "static"
8073             });
8074             return this;
8075         },
8076
8077         /**
8078         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8079         * snapshot before performing an update and then restoring the element.
8080         * @return {Object}
8081         */
8082         getPositioning : function(){
8083             var l = this.getStyle("left");
8084             var t = this.getStyle("top");
8085             return {
8086                 "position" : this.getStyle("position"),
8087                 "left" : l,
8088                 "right" : l ? "" : this.getStyle("right"),
8089                 "top" : t,
8090                 "bottom" : t ? "" : this.getStyle("bottom"),
8091                 "z-index" : this.getStyle("z-index")
8092             };
8093         },
8094
8095         /**
8096          * Gets the width of the border(s) for the specified side(s)
8097          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8098          * passing lr would get the border (l)eft width + the border (r)ight width.
8099          * @return {Number} The width of the sides passed added together
8100          */
8101         getBorderWidth : function(side){
8102             return this.addStyles(side, El.borders);
8103         },
8104
8105         /**
8106          * Gets the width of the padding(s) for the specified side(s)
8107          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8108          * passing lr would get the padding (l)eft + the padding (r)ight.
8109          * @return {Number} The padding of the sides passed added together
8110          */
8111         getPadding : function(side){
8112             return this.addStyles(side, El.paddings);
8113         },
8114
8115         /**
8116         * Set positioning with an object returned by getPositioning().
8117         * @param {Object} posCfg
8118         * @return {Roo.Element} this
8119          */
8120         setPositioning : function(pc){
8121             this.applyStyles(pc);
8122             if(pc.right == "auto"){
8123                 this.dom.style.right = "";
8124             }
8125             if(pc.bottom == "auto"){
8126                 this.dom.style.bottom = "";
8127             }
8128             return this;
8129         },
8130
8131         // private
8132         fixDisplay : function(){
8133             if(this.getStyle("display") == "none"){
8134                 this.setStyle("visibility", "hidden");
8135                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8136                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8137                     this.setStyle("display", "block");
8138                 }
8139             }
8140         },
8141
8142         /**
8143          * Quick set left and top adding default units
8144          * @param {String} left The left CSS property value
8145          * @param {String} top The top CSS property value
8146          * @return {Roo.Element} this
8147          */
8148          setLeftTop : function(left, top){
8149             this.dom.style.left = this.addUnits(left);
8150             this.dom.style.top = this.addUnits(top);
8151             return this;
8152         },
8153
8154         /**
8155          * Move this element relative to its current position.
8156          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8157          * @param {Number} distance How far to move the element in pixels
8158          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8159          * @return {Roo.Element} this
8160          */
8161          move : function(direction, distance, animate){
8162             var xy = this.getXY();
8163             direction = direction.toLowerCase();
8164             switch(direction){
8165                 case "l":
8166                 case "left":
8167                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8168                     break;
8169                case "r":
8170                case "right":
8171                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8172                     break;
8173                case "t":
8174                case "top":
8175                case "up":
8176                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8177                     break;
8178                case "b":
8179                case "bottom":
8180                case "down":
8181                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8182                     break;
8183             }
8184             return this;
8185         },
8186
8187         /**
8188          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8189          * @return {Roo.Element} this
8190          */
8191         clip : function(){
8192             if(!this.isClipped){
8193                this.isClipped = true;
8194                this.originalClip = {
8195                    "o": this.getStyle("overflow"),
8196                    "x": this.getStyle("overflow-x"),
8197                    "y": this.getStyle("overflow-y")
8198                };
8199                this.setStyle("overflow", "hidden");
8200                this.setStyle("overflow-x", "hidden");
8201                this.setStyle("overflow-y", "hidden");
8202             }
8203             return this;
8204         },
8205
8206         /**
8207          *  Return clipping (overflow) to original clipping before clip() was called
8208          * @return {Roo.Element} this
8209          */
8210         unclip : function(){
8211             if(this.isClipped){
8212                 this.isClipped = false;
8213                 var o = this.originalClip;
8214                 if(o.o){this.setStyle("overflow", o.o);}
8215                 if(o.x){this.setStyle("overflow-x", o.x);}
8216                 if(o.y){this.setStyle("overflow-y", o.y);}
8217             }
8218             return this;
8219         },
8220
8221
8222         /**
8223          * Gets the x,y coordinates specified by the anchor position on the element.
8224          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8225          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8226          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8227          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8228          * @return {Array} [x, y] An array containing the element's x and y coordinates
8229          */
8230         getAnchorXY : function(anchor, local, s){
8231             //Passing a different size is useful for pre-calculating anchors,
8232             //especially for anchored animations that change the el size.
8233
8234             var w, h, vp = false;
8235             if(!s){
8236                 var d = this.dom;
8237                 if(d == document.body || d == document){
8238                     vp = true;
8239                     w = D.getViewWidth(); h = D.getViewHeight();
8240                 }else{
8241                     w = this.getWidth(); h = this.getHeight();
8242                 }
8243             }else{
8244                 w = s.width;  h = s.height;
8245             }
8246             var x = 0, y = 0, r = Math.round;
8247             switch((anchor || "tl").toLowerCase()){
8248                 case "c":
8249                     x = r(w*.5);
8250                     y = r(h*.5);
8251                 break;
8252                 case "t":
8253                     x = r(w*.5);
8254                     y = 0;
8255                 break;
8256                 case "l":
8257                     x = 0;
8258                     y = r(h*.5);
8259                 break;
8260                 case "r":
8261                     x = w;
8262                     y = r(h*.5);
8263                 break;
8264                 case "b":
8265                     x = r(w*.5);
8266                     y = h;
8267                 break;
8268                 case "tl":
8269                     x = 0;
8270                     y = 0;
8271                 break;
8272                 case "bl":
8273                     x = 0;
8274                     y = h;
8275                 break;
8276                 case "br":
8277                     x = w;
8278                     y = h;
8279                 break;
8280                 case "tr":
8281                     x = w;
8282                     y = 0;
8283                 break;
8284             }
8285             if(local === true){
8286                 return [x, y];
8287             }
8288             if(vp){
8289                 var sc = this.getScroll();
8290                 return [x + sc.left, y + sc.top];
8291             }
8292             //Add the element's offset xy
8293             var o = this.getXY();
8294             return [x+o[0], y+o[1]];
8295         },
8296
8297         /**
8298          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8299          * supported position values.
8300          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8301          * @param {String} position The position to align to.
8302          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8303          * @return {Array} [x, y]
8304          */
8305         getAlignToXY : function(el, p, o){
8306             el = Roo.get(el);
8307             var d = this.dom;
8308             if(!el.dom){
8309                 throw "Element.alignTo with an element that doesn't exist";
8310             }
8311             var c = false; //constrain to viewport
8312             var p1 = "", p2 = "";
8313             o = o || [0,0];
8314
8315             if(!p){
8316                 p = "tl-bl";
8317             }else if(p == "?"){
8318                 p = "tl-bl?";
8319             }else if(p.indexOf("-") == -1){
8320                 p = "tl-" + p;
8321             }
8322             p = p.toLowerCase();
8323             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8324             if(!m){
8325                throw "Element.alignTo with an invalid alignment " + p;
8326             }
8327             p1 = m[1]; p2 = m[2]; c = !!m[3];
8328
8329             //Subtract the aligned el's internal xy from the target's offset xy
8330             //plus custom offset to get the aligned el's new offset xy
8331             var a1 = this.getAnchorXY(p1, true);
8332             var a2 = el.getAnchorXY(p2, false);
8333             var x = a2[0] - a1[0] + o[0];
8334             var y = a2[1] - a1[1] + o[1];
8335             if(c){
8336                 //constrain the aligned el to viewport if necessary
8337                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8338                 // 5px of margin for ie
8339                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8340
8341                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8342                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8343                 //otherwise swap the aligned el to the opposite border of the target.
8344                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8345                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8346                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8347                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8348
8349                var doc = document;
8350                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8351                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8352
8353                if((x+w) > dw + scrollX){
8354                     x = swapX ? r.left-w : dw+scrollX-w;
8355                 }
8356                if(x < scrollX){
8357                    x = swapX ? r.right : scrollX;
8358                }
8359                if((y+h) > dh + scrollY){
8360                     y = swapY ? r.top-h : dh+scrollY-h;
8361                 }
8362                if (y < scrollY){
8363                    y = swapY ? r.bottom : scrollY;
8364                }
8365             }
8366             return [x,y];
8367         },
8368
8369         // private
8370         getConstrainToXY : function(){
8371             var os = {top:0, left:0, bottom:0, right: 0};
8372
8373             return function(el, local, offsets, proposedXY){
8374                 el = Roo.get(el);
8375                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8376
8377                 var vw, vh, vx = 0, vy = 0;
8378                 if(el.dom == document.body || el.dom == document){
8379                     vw = Roo.lib.Dom.getViewWidth();
8380                     vh = Roo.lib.Dom.getViewHeight();
8381                 }else{
8382                     vw = el.dom.clientWidth;
8383                     vh = el.dom.clientHeight;
8384                     if(!local){
8385                         var vxy = el.getXY();
8386                         vx = vxy[0];
8387                         vy = vxy[1];
8388                     }
8389                 }
8390
8391                 var s = el.getScroll();
8392
8393                 vx += offsets.left + s.left;
8394                 vy += offsets.top + s.top;
8395
8396                 vw -= offsets.right;
8397                 vh -= offsets.bottom;
8398
8399                 var vr = vx+vw;
8400                 var vb = vy+vh;
8401
8402                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8403                 var x = xy[0], y = xy[1];
8404                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8405
8406                 // only move it if it needs it
8407                 var moved = false;
8408
8409                 // first validate right/bottom
8410                 if((x + w) > vr){
8411                     x = vr - w;
8412                     moved = true;
8413                 }
8414                 if((y + h) > vb){
8415                     y = vb - h;
8416                     moved = true;
8417                 }
8418                 // then make sure top/left isn't negative
8419                 if(x < vx){
8420                     x = vx;
8421                     moved = true;
8422                 }
8423                 if(y < vy){
8424                     y = vy;
8425                     moved = true;
8426                 }
8427                 return moved ? [x, y] : false;
8428             };
8429         }(),
8430
8431         // private
8432         adjustForConstraints : function(xy, parent, offsets){
8433             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8434         },
8435
8436         /**
8437          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8438          * document it aligns it to the viewport.
8439          * The position parameter is optional, and can be specified in any one of the following formats:
8440          * <ul>
8441          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8442          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8443          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8444          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8445          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8446          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8447          * </ul>
8448          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8449          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8450          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8451          * that specified in order to enforce the viewport constraints.
8452          * Following are all of the supported anchor positions:
8453     <pre>
8454     Value  Description
8455     -----  -----------------------------
8456     tl     The top left corner (default)
8457     t      The center of the top edge
8458     tr     The top right corner
8459     l      The center of the left edge
8460     c      In the center of the element
8461     r      The center of the right edge
8462     bl     The bottom left corner
8463     b      The center of the bottom edge
8464     br     The bottom right corner
8465     </pre>
8466     Example Usage:
8467     <pre><code>
8468     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8469     el.alignTo("other-el");
8470
8471     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8472     el.alignTo("other-el", "tr?");
8473
8474     // align the bottom right corner of el with the center left edge of other-el
8475     el.alignTo("other-el", "br-l?");
8476
8477     // align the center of el with the bottom left corner of other-el and
8478     // adjust the x position by -6 pixels (and the y position by 0)
8479     el.alignTo("other-el", "c-bl", [-6, 0]);
8480     </code></pre>
8481          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8482          * @param {String} position The position to align to.
8483          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8484          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8485          * @return {Roo.Element} this
8486          */
8487         alignTo : function(element, position, offsets, animate){
8488             var xy = this.getAlignToXY(element, position, offsets);
8489             this.setXY(xy, this.preanim(arguments, 3));
8490             return this;
8491         },
8492
8493         /**
8494          * Anchors an element to another element and realigns it when the window is resized.
8495          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8496          * @param {String} position The position to align to.
8497          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8498          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8499          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8500          * is a number, it is used as the buffer delay (defaults to 50ms).
8501          * @param {Function} callback The function to call after the animation finishes
8502          * @return {Roo.Element} this
8503          */
8504         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8505             var action = function(){
8506                 this.alignTo(el, alignment, offsets, animate);
8507                 Roo.callback(callback, this);
8508             };
8509             Roo.EventManager.onWindowResize(action, this);
8510             var tm = typeof monitorScroll;
8511             if(tm != 'undefined'){
8512                 Roo.EventManager.on(window, 'scroll', action, this,
8513                     {buffer: tm == 'number' ? monitorScroll : 50});
8514             }
8515             action.call(this); // align immediately
8516             return this;
8517         },
8518         /**
8519          * Clears any opacity settings from this element. Required in some cases for IE.
8520          * @return {Roo.Element} this
8521          */
8522         clearOpacity : function(){
8523             if (window.ActiveXObject) {
8524                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8525                     this.dom.style.filter = "";
8526                 }
8527             } else {
8528                 this.dom.style.opacity = "";
8529                 this.dom.style["-moz-opacity"] = "";
8530                 this.dom.style["-khtml-opacity"] = "";
8531             }
8532             return this;
8533         },
8534
8535         /**
8536          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8537          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8538          * @return {Roo.Element} this
8539          */
8540         hide : function(animate){
8541             this.setVisible(false, this.preanim(arguments, 0));
8542             return this;
8543         },
8544
8545         /**
8546         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8547         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8548          * @return {Roo.Element} this
8549          */
8550         show : function(animate){
8551             this.setVisible(true, this.preanim(arguments, 0));
8552             return this;
8553         },
8554
8555         /**
8556          * @private Test if size has a unit, otherwise appends the default
8557          */
8558         addUnits : function(size){
8559             return Roo.Element.addUnits(size, this.defaultUnit);
8560         },
8561
8562         /**
8563          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8564          * @return {Roo.Element} this
8565          */
8566         beginMeasure : function(){
8567             var el = this.dom;
8568             if(el.offsetWidth || el.offsetHeight){
8569                 return this; // offsets work already
8570             }
8571             var changed = [];
8572             var p = this.dom, b = document.body; // start with this element
8573             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8574                 var pe = Roo.get(p);
8575                 if(pe.getStyle('display') == 'none'){
8576                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8577                     p.style.visibility = "hidden";
8578                     p.style.display = "block";
8579                 }
8580                 p = p.parentNode;
8581             }
8582             this._measureChanged = changed;
8583             return this;
8584
8585         },
8586
8587         /**
8588          * Restores displays to before beginMeasure was called
8589          * @return {Roo.Element} this
8590          */
8591         endMeasure : function(){
8592             var changed = this._measureChanged;
8593             if(changed){
8594                 for(var i = 0, len = changed.length; i < len; i++) {
8595                     var r = changed[i];
8596                     r.el.style.visibility = r.visibility;
8597                     r.el.style.display = "none";
8598                 }
8599                 this._measureChanged = null;
8600             }
8601             return this;
8602         },
8603
8604         /**
8605         * Update the innerHTML of this element, optionally searching for and processing scripts
8606         * @param {String} html The new HTML
8607         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8608         * @param {Function} callback For async script loading you can be noticed when the update completes
8609         * @return {Roo.Element} this
8610          */
8611         update : function(html, loadScripts, callback){
8612             if(typeof html == "undefined"){
8613                 html = "";
8614             }
8615             if(loadScripts !== true){
8616                 this.dom.innerHTML = html;
8617                 if(typeof callback == "function"){
8618                     callback();
8619                 }
8620                 return this;
8621             }
8622             var id = Roo.id();
8623             var dom = this.dom;
8624
8625             html += '<span id="' + id + '"></span>';
8626
8627             E.onAvailable(id, function(){
8628                 var hd = document.getElementsByTagName("head")[0];
8629                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8630                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8631                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8632
8633                 var match;
8634                 while(match = re.exec(html)){
8635                     var attrs = match[1];
8636                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8637                     if(srcMatch && srcMatch[2]){
8638                        var s = document.createElement("script");
8639                        s.src = srcMatch[2];
8640                        var typeMatch = attrs.match(typeRe);
8641                        if(typeMatch && typeMatch[2]){
8642                            s.type = typeMatch[2];
8643                        }
8644                        hd.appendChild(s);
8645                     }else if(match[2] && match[2].length > 0){
8646                         if(window.execScript) {
8647                            window.execScript(match[2]);
8648                         } else {
8649                             /**
8650                              * eval:var:id
8651                              * eval:var:dom
8652                              * eval:var:html
8653                              * 
8654                              */
8655                            window.eval(match[2]);
8656                         }
8657                     }
8658                 }
8659                 var el = document.getElementById(id);
8660                 if(el){el.parentNode.removeChild(el);}
8661                 if(typeof callback == "function"){
8662                     callback();
8663                 }
8664             });
8665             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8666             return this;
8667         },
8668
8669         /**
8670          * Direct access to the UpdateManager update() method (takes the same parameters).
8671          * @param {String/Function} url The url for this request or a function to call to get the url
8672          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8673          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8674          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8675          * @return {Roo.Element} this
8676          */
8677         load : function(){
8678             var um = this.getUpdateManager();
8679             um.update.apply(um, arguments);
8680             return this;
8681         },
8682
8683         /**
8684         * Gets this element's UpdateManager
8685         * @return {Roo.UpdateManager} The UpdateManager
8686         */
8687         getUpdateManager : function(){
8688             if(!this.updateManager){
8689                 this.updateManager = new Roo.UpdateManager(this);
8690             }
8691             return this.updateManager;
8692         },
8693
8694         /**
8695          * Disables text selection for this element (normalized across browsers)
8696          * @return {Roo.Element} this
8697          */
8698         unselectable : function(){
8699             this.dom.unselectable = "on";
8700             this.swallowEvent("selectstart", true);
8701             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8702             this.addClass("x-unselectable");
8703             return this;
8704         },
8705
8706         /**
8707         * Calculates the x, y to center this element on the screen
8708         * @return {Array} The x, y values [x, y]
8709         */
8710         getCenterXY : function(){
8711             return this.getAlignToXY(document, 'c-c');
8712         },
8713
8714         /**
8715         * Centers the Element in either the viewport, or another Element.
8716         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8717         */
8718         center : function(centerIn){
8719             this.alignTo(centerIn || document, 'c-c');
8720             return this;
8721         },
8722
8723         /**
8724          * Tests various css rules/browsers to determine if this element uses a border box
8725          * @return {Boolean}
8726          */
8727         isBorderBox : function(){
8728             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8729         },
8730
8731         /**
8732          * Return a box {x, y, width, height} that can be used to set another elements
8733          * size/location to match this element.
8734          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8735          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8736          * @return {Object} box An object in the format {x, y, width, height}
8737          */
8738         getBox : function(contentBox, local){
8739             var xy;
8740             if(!local){
8741                 xy = this.getXY();
8742             }else{
8743                 var left = parseInt(this.getStyle("left"), 10) || 0;
8744                 var top = parseInt(this.getStyle("top"), 10) || 0;
8745                 xy = [left, top];
8746             }
8747             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8748             if(!contentBox){
8749                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8750             }else{
8751                 var l = this.getBorderWidth("l")+this.getPadding("l");
8752                 var r = this.getBorderWidth("r")+this.getPadding("r");
8753                 var t = this.getBorderWidth("t")+this.getPadding("t");
8754                 var b = this.getBorderWidth("b")+this.getPadding("b");
8755                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8756             }
8757             bx.right = bx.x + bx.width;
8758             bx.bottom = bx.y + bx.height;
8759             return bx;
8760         },
8761
8762         /**
8763          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8764          for more information about the sides.
8765          * @param {String} sides
8766          * @return {Number}
8767          */
8768         getFrameWidth : function(sides, onlyContentBox){
8769             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8770         },
8771
8772         /**
8773          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8774          * @param {Object} box The box to fill {x, y, width, height}
8775          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8776          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8777          * @return {Roo.Element} this
8778          */
8779         setBox : function(box, adjust, animate){
8780             var w = box.width, h = box.height;
8781             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8782                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8783                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8784             }
8785             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8786             return this;
8787         },
8788
8789         /**
8790          * Forces the browser to repaint this element
8791          * @return {Roo.Element} this
8792          */
8793          repaint : function(){
8794             var dom = this.dom;
8795             this.addClass("x-repaint");
8796             setTimeout(function(){
8797                 Roo.get(dom).removeClass("x-repaint");
8798             }, 1);
8799             return this;
8800         },
8801
8802         /**
8803          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8804          * then it returns the calculated width of the sides (see getPadding)
8805          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8806          * @return {Object/Number}
8807          */
8808         getMargins : function(side){
8809             if(!side){
8810                 return {
8811                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8812                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8813                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8814                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8815                 };
8816             }else{
8817                 return this.addStyles(side, El.margins);
8818              }
8819         },
8820
8821         // private
8822         addStyles : function(sides, styles){
8823             var val = 0, v, w;
8824             for(var i = 0, len = sides.length; i < len; i++){
8825                 v = this.getStyle(styles[sides.charAt(i)]);
8826                 if(v){
8827                      w = parseInt(v, 10);
8828                      if(w){ val += w; }
8829                 }
8830             }
8831             return val;
8832         },
8833
8834         /**
8835          * Creates a proxy element of this element
8836          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8837          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8838          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8839          * @return {Roo.Element} The new proxy element
8840          */
8841         createProxy : function(config, renderTo, matchBox){
8842             if(renderTo){
8843                 renderTo = Roo.getDom(renderTo);
8844             }else{
8845                 renderTo = document.body;
8846             }
8847             config = typeof config == "object" ?
8848                 config : {tag : "div", cls: config};
8849             var proxy = Roo.DomHelper.append(renderTo, config, true);
8850             if(matchBox){
8851                proxy.setBox(this.getBox());
8852             }
8853             return proxy;
8854         },
8855
8856         /**
8857          * Puts a mask over this element to disable user interaction. Requires core.css.
8858          * This method can only be applied to elements which accept child nodes.
8859          * @param {String} msg (optional) A message to display in the mask
8860          * @param {String} msgCls (optional) A css class to apply to the msg element
8861          * @return {Element} The mask  element
8862          */
8863         mask : function(msg, msgCls){
8864             if(this.getStyle("position") == "static"){
8865                 this.setStyle("position", "relative");
8866             }
8867             if(!this._mask){
8868                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8869             }
8870             this.addClass("x-masked");
8871             this._mask.setDisplayed(true);
8872             if(typeof msg == 'string'){
8873                 if(!this._maskMsg){
8874                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8875                 }
8876                 var mm = this._maskMsg;
8877                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8878                 mm.dom.firstChild.innerHTML = msg;
8879                 mm.setDisplayed(true);
8880                 mm.center(this);
8881             }
8882             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8883                 this._mask.setHeight(this.getHeight());
8884             }
8885             return this._mask;
8886         },
8887
8888         /**
8889          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8890          * it is cached for reuse.
8891          */
8892         unmask : function(removeEl){
8893             if(this._mask){
8894                 if(removeEl === true){
8895                     this._mask.remove();
8896                     delete this._mask;
8897                     if(this._maskMsg){
8898                         this._maskMsg.remove();
8899                         delete this._maskMsg;
8900                     }
8901                 }else{
8902                     this._mask.setDisplayed(false);
8903                     if(this._maskMsg){
8904                         this._maskMsg.setDisplayed(false);
8905                     }
8906                 }
8907             }
8908             this.removeClass("x-masked");
8909         },
8910
8911         /**
8912          * Returns true if this element is masked
8913          * @return {Boolean}
8914          */
8915         isMasked : function(){
8916             return this._mask && this._mask.isVisible();
8917         },
8918
8919         /**
8920          * Creates an iframe shim for this element to keep selects and other windowed objects from
8921          * showing through.
8922          * @return {Roo.Element} The new shim element
8923          */
8924         createShim : function(){
8925             var el = document.createElement('iframe');
8926             el.frameBorder = 'no';
8927             el.className = 'roo-shim';
8928             if(Roo.isIE && Roo.isSecure){
8929                 el.src = Roo.SSL_SECURE_URL;
8930             }
8931             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8932             shim.autoBoxAdjust = false;
8933             return shim;
8934         },
8935
8936         /**
8937          * Removes this element from the DOM and deletes it from the cache
8938          */
8939         remove : function(){
8940             if(this.dom.parentNode){
8941                 this.dom.parentNode.removeChild(this.dom);
8942             }
8943             delete El.cache[this.dom.id];
8944         },
8945
8946         /**
8947          * Sets up event handlers to add and remove a css class when the mouse is over this element
8948          * @param {String} className
8949          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8950          * mouseout events for children elements
8951          * @return {Roo.Element} this
8952          */
8953         addClassOnOver : function(className, preventFlicker){
8954             this.on("mouseover", function(){
8955                 Roo.fly(this, '_internal').addClass(className);
8956             }, this.dom);
8957             var removeFn = function(e){
8958                 if(preventFlicker !== true || !e.within(this, true)){
8959                     Roo.fly(this, '_internal').removeClass(className);
8960                 }
8961             };
8962             this.on("mouseout", removeFn, this.dom);
8963             return this;
8964         },
8965
8966         /**
8967          * Sets up event handlers to add and remove a css class when this element has the focus
8968          * @param {String} className
8969          * @return {Roo.Element} this
8970          */
8971         addClassOnFocus : function(className){
8972             this.on("focus", function(){
8973                 Roo.fly(this, '_internal').addClass(className);
8974             }, this.dom);
8975             this.on("blur", function(){
8976                 Roo.fly(this, '_internal').removeClass(className);
8977             }, this.dom);
8978             return this;
8979         },
8980         /**
8981          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
8982          * @param {String} className
8983          * @return {Roo.Element} this
8984          */
8985         addClassOnClick : function(className){
8986             var dom = this.dom;
8987             this.on("mousedown", function(){
8988                 Roo.fly(dom, '_internal').addClass(className);
8989                 var d = Roo.get(document);
8990                 var fn = function(){
8991                     Roo.fly(dom, '_internal').removeClass(className);
8992                     d.removeListener("mouseup", fn);
8993                 };
8994                 d.on("mouseup", fn);
8995             });
8996             return this;
8997         },
8998
8999         /**
9000          * Stops the specified event from bubbling and optionally prevents the default action
9001          * @param {String} eventName
9002          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9003          * @return {Roo.Element} this
9004          */
9005         swallowEvent : function(eventName, preventDefault){
9006             var fn = function(e){
9007                 e.stopPropagation();
9008                 if(preventDefault){
9009                     e.preventDefault();
9010                 }
9011             };
9012             if(eventName instanceof Array){
9013                 for(var i = 0, len = eventName.length; i < len; i++){
9014                      this.on(eventName[i], fn);
9015                 }
9016                 return this;
9017             }
9018             this.on(eventName, fn);
9019             return this;
9020         },
9021
9022         /**
9023          * @private
9024          */
9025       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9026
9027         /**
9028          * Sizes this element to its parent element's dimensions performing
9029          * neccessary box adjustments.
9030          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9031          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9032          * @return {Roo.Element} this
9033          */
9034         fitToParent : function(monitorResize, targetParent) {
9035           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9036           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9037           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9038             return;
9039           }
9040           var p = Roo.get(targetParent || this.dom.parentNode);
9041           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9042           if (monitorResize === true) {
9043             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9044             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9045           }
9046           return this;
9047         },
9048
9049         /**
9050          * Gets the next sibling, skipping text nodes
9051          * @return {HTMLElement} The next sibling or null
9052          */
9053         getNextSibling : function(){
9054             var n = this.dom.nextSibling;
9055             while(n && n.nodeType != 1){
9056                 n = n.nextSibling;
9057             }
9058             return n;
9059         },
9060
9061         /**
9062          * Gets the previous sibling, skipping text nodes
9063          * @return {HTMLElement} The previous sibling or null
9064          */
9065         getPrevSibling : function(){
9066             var n = this.dom.previousSibling;
9067             while(n && n.nodeType != 1){
9068                 n = n.previousSibling;
9069             }
9070             return n;
9071         },
9072
9073
9074         /**
9075          * Appends the passed element(s) to this element
9076          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9077          * @return {Roo.Element} this
9078          */
9079         appendChild: function(el){
9080             el = Roo.get(el);
9081             el.appendTo(this);
9082             return this;
9083         },
9084
9085         /**
9086          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9087          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9088          * automatically generated with the specified attributes.
9089          * @param {HTMLElement} insertBefore (optional) a child element of this element
9090          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9091          * @return {Roo.Element} The new child element
9092          */
9093         createChild: function(config, insertBefore, returnDom){
9094             config = config || {tag:'div'};
9095             if(insertBefore){
9096                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9097             }
9098             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9099         },
9100
9101         /**
9102          * Appends this element to the passed element
9103          * @param {String/HTMLElement/Element} el The new parent element
9104          * @return {Roo.Element} this
9105          */
9106         appendTo: function(el){
9107             el = Roo.getDom(el);
9108             el.appendChild(this.dom);
9109             return this;
9110         },
9111
9112         /**
9113          * Inserts this element before the passed element in the DOM
9114          * @param {String/HTMLElement/Element} el The element to insert before
9115          * @return {Roo.Element} this
9116          */
9117         insertBefore: function(el){
9118             el = Roo.getDom(el);
9119             el.parentNode.insertBefore(this.dom, el);
9120             return this;
9121         },
9122
9123         /**
9124          * Inserts this element after the passed element in the DOM
9125          * @param {String/HTMLElement/Element} el The element to insert after
9126          * @return {Roo.Element} this
9127          */
9128         insertAfter: function(el){
9129             el = Roo.getDom(el);
9130             el.parentNode.insertBefore(this.dom, el.nextSibling);
9131             return this;
9132         },
9133
9134         /**
9135          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9136          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9137          * @return {Roo.Element} The new child
9138          */
9139         insertFirst: function(el, returnDom){
9140             el = el || {};
9141             if(typeof el == 'object' && !el.nodeType){ // dh config
9142                 return this.createChild(el, this.dom.firstChild, returnDom);
9143             }else{
9144                 el = Roo.getDom(el);
9145                 this.dom.insertBefore(el, this.dom.firstChild);
9146                 return !returnDom ? Roo.get(el) : el;
9147             }
9148         },
9149
9150         /**
9151          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9152          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9153          * @param {String} where (optional) 'before' or 'after' defaults to before
9154          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9155          * @return {Roo.Element} the inserted Element
9156          */
9157         insertSibling: function(el, where, returnDom){
9158             where = where ? where.toLowerCase() : 'before';
9159             el = el || {};
9160             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9161
9162             if(typeof el == 'object' && !el.nodeType){ // dh config
9163                 if(where == 'after' && !this.dom.nextSibling){
9164                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9165                 }else{
9166                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9167                 }
9168
9169             }else{
9170                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9171                             where == 'before' ? this.dom : this.dom.nextSibling);
9172                 if(!returnDom){
9173                     rt = Roo.get(rt);
9174                 }
9175             }
9176             return rt;
9177         },
9178
9179         /**
9180          * Creates and wraps this element with another element
9181          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9182          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9183          * @return {HTMLElement/Element} The newly created wrapper element
9184          */
9185         wrap: function(config, returnDom){
9186             if(!config){
9187                 config = {tag: "div"};
9188             }
9189             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9190             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9191             return newEl;
9192         },
9193
9194         /**
9195          * Replaces the passed element with this element
9196          * @param {String/HTMLElement/Element} el The element to replace
9197          * @return {Roo.Element} this
9198          */
9199         replace: function(el){
9200             el = Roo.get(el);
9201             this.insertBefore(el);
9202             el.remove();
9203             return this;
9204         },
9205
9206         /**
9207          * Inserts an html fragment into this element
9208          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9209          * @param {String} html The HTML fragment
9210          * @param {Boolean} returnEl True to return an Roo.Element
9211          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9212          */
9213         insertHtml : function(where, html, returnEl){
9214             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9215             return returnEl ? Roo.get(el) : el;
9216         },
9217
9218         /**
9219          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9220          * @param {Object} o The object with the attributes
9221          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9222          * @return {Roo.Element} this
9223          */
9224         set : function(o, useSet){
9225             var el = this.dom;
9226             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9227             for(var attr in o){
9228                 if(attr == "style" || typeof o[attr] == "function") continue;
9229                 if(attr=="cls"){
9230                     el.className = o["cls"];
9231                 }else{
9232                     if(useSet) el.setAttribute(attr, o[attr]);
9233                     else el[attr] = o[attr];
9234                 }
9235             }
9236             if(o.style){
9237                 Roo.DomHelper.applyStyles(el, o.style);
9238             }
9239             return this;
9240         },
9241
9242         /**
9243          * Convenience method for constructing a KeyMap
9244          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9245          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9246          * @param {Function} fn The function to call
9247          * @param {Object} scope (optional) The scope of the function
9248          * @return {Roo.KeyMap} The KeyMap created
9249          */
9250         addKeyListener : function(key, fn, scope){
9251             var config;
9252             if(typeof key != "object" || key instanceof Array){
9253                 config = {
9254                     key: key,
9255                     fn: fn,
9256                     scope: scope
9257                 };
9258             }else{
9259                 config = {
9260                     key : key.key,
9261                     shift : key.shift,
9262                     ctrl : key.ctrl,
9263                     alt : key.alt,
9264                     fn: fn,
9265                     scope: scope
9266                 };
9267             }
9268             return new Roo.KeyMap(this, config);
9269         },
9270
9271         /**
9272          * Creates a KeyMap for this element
9273          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9274          * @return {Roo.KeyMap} The KeyMap created
9275          */
9276         addKeyMap : function(config){
9277             return new Roo.KeyMap(this, config);
9278         },
9279
9280         /**
9281          * Returns true if this element is scrollable.
9282          * @return {Boolean}
9283          */
9284          isScrollable : function(){
9285             var dom = this.dom;
9286             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9287         },
9288
9289         /**
9290          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9291          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9292          * @param {Number} value The new scroll value
9293          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9294          * @return {Element} this
9295          */
9296
9297         scrollTo : function(side, value, animate){
9298             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9299             if(!animate || !A){
9300                 this.dom[prop] = value;
9301             }else{
9302                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9303                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9304             }
9305             return this;
9306         },
9307
9308         /**
9309          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9310          * within this element's scrollable range.
9311          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9312          * @param {Number} distance How far to scroll the element in pixels
9313          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9314          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9315          * was scrolled as far as it could go.
9316          */
9317          scroll : function(direction, distance, animate){
9318              if(!this.isScrollable()){
9319                  return;
9320              }
9321              var el = this.dom;
9322              var l = el.scrollLeft, t = el.scrollTop;
9323              var w = el.scrollWidth, h = el.scrollHeight;
9324              var cw = el.clientWidth, ch = el.clientHeight;
9325              direction = direction.toLowerCase();
9326              var scrolled = false;
9327              var a = this.preanim(arguments, 2);
9328              switch(direction){
9329                  case "l":
9330                  case "left":
9331                      if(w - l > cw){
9332                          var v = Math.min(l + distance, w-cw);
9333                          this.scrollTo("left", v, a);
9334                          scrolled = true;
9335                      }
9336                      break;
9337                 case "r":
9338                 case "right":
9339                      if(l > 0){
9340                          var v = Math.max(l - distance, 0);
9341                          this.scrollTo("left", v, a);
9342                          scrolled = true;
9343                      }
9344                      break;
9345                 case "t":
9346                 case "top":
9347                 case "up":
9348                      if(t > 0){
9349                          var v = Math.max(t - distance, 0);
9350                          this.scrollTo("top", v, a);
9351                          scrolled = true;
9352                      }
9353                      break;
9354                 case "b":
9355                 case "bottom":
9356                 case "down":
9357                      if(h - t > ch){
9358                          var v = Math.min(t + distance, h-ch);
9359                          this.scrollTo("top", v, a);
9360                          scrolled = true;
9361                      }
9362                      break;
9363              }
9364              return scrolled;
9365         },
9366
9367         /**
9368          * Translates the passed page coordinates into left/top css values for this element
9369          * @param {Number/Array} x The page x or an array containing [x, y]
9370          * @param {Number} y The page y
9371          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9372          */
9373         translatePoints : function(x, y){
9374             if(typeof x == 'object' || x instanceof Array){
9375                 y = x[1]; x = x[0];
9376             }
9377             var p = this.getStyle('position');
9378             var o = this.getXY();
9379
9380             var l = parseInt(this.getStyle('left'), 10);
9381             var t = parseInt(this.getStyle('top'), 10);
9382
9383             if(isNaN(l)){
9384                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9385             }
9386             if(isNaN(t)){
9387                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9388             }
9389
9390             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9391         },
9392
9393         /**
9394          * Returns the current scroll position of the element.
9395          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9396          */
9397         getScroll : function(){
9398             var d = this.dom, doc = document;
9399             if(d == doc || d == doc.body){
9400                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9401                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9402                 return {left: l, top: t};
9403             }else{
9404                 return {left: d.scrollLeft, top: d.scrollTop};
9405             }
9406         },
9407
9408         /**
9409          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9410          * are convert to standard 6 digit hex color.
9411          * @param {String} attr The css attribute
9412          * @param {String} defaultValue The default value to use when a valid color isn't found
9413          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9414          * YUI color anims.
9415          */
9416         getColor : function(attr, defaultValue, prefix){
9417             var v = this.getStyle(attr);
9418             if(!v || v == "transparent" || v == "inherit") {
9419                 return defaultValue;
9420             }
9421             var color = typeof prefix == "undefined" ? "#" : prefix;
9422             if(v.substr(0, 4) == "rgb("){
9423                 var rvs = v.slice(4, v.length -1).split(",");
9424                 for(var i = 0; i < 3; i++){
9425                     var h = parseInt(rvs[i]).toString(16);
9426                     if(h < 16){
9427                         h = "0" + h;
9428                     }
9429                     color += h;
9430                 }
9431             } else {
9432                 if(v.substr(0, 1) == "#"){
9433                     if(v.length == 4) {
9434                         for(var i = 1; i < 4; i++){
9435                             var c = v.charAt(i);
9436                             color +=  c + c;
9437                         }
9438                     }else if(v.length == 7){
9439                         color += v.substr(1);
9440                     }
9441                 }
9442             }
9443             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9444         },
9445
9446         /**
9447          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9448          * gradient background, rounded corners and a 4-way shadow.
9449          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9450          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9451          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9452          * @return {Roo.Element} this
9453          */
9454         boxWrap : function(cls){
9455             cls = cls || 'x-box';
9456             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9457             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9458             return el;
9459         },
9460
9461         /**
9462          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9463          * @param {String} namespace The namespace in which to look for the attribute
9464          * @param {String} name The attribute name
9465          * @return {String} The attribute value
9466          */
9467         getAttributeNS : Roo.isIE ? function(ns, name){
9468             var d = this.dom;
9469             var type = typeof d[ns+":"+name];
9470             if(type != 'undefined' && type != 'unknown'){
9471                 return d[ns+":"+name];
9472             }
9473             return d[name];
9474         } : function(ns, name){
9475             var d = this.dom;
9476             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9477         }
9478     };
9479
9480     var ep = El.prototype;
9481
9482     /**
9483      * Appends an event handler (Shorthand for addListener)
9484      * @param {String}   eventName     The type of event to append
9485      * @param {Function} fn        The method the event invokes
9486      * @param {Object} scope       (optional) The scope (this object) of the fn
9487      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9488      * @method
9489      */
9490     ep.on = ep.addListener;
9491         // backwards compat
9492     ep.mon = ep.addListener;
9493
9494     /**
9495      * Removes an event handler from this element (shorthand for removeListener)
9496      * @param {String} eventName the type of event to remove
9497      * @param {Function} fn the method the event invokes
9498      * @return {Roo.Element} this
9499      * @method
9500      */
9501     ep.un = ep.removeListener;
9502
9503     /**
9504      * true to automatically adjust width and height settings for box-model issues (default to true)
9505      */
9506     ep.autoBoxAdjust = true;
9507
9508     // private
9509     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9510
9511     // private
9512     El.addUnits = function(v, defaultUnit){
9513         if(v === "" || v == "auto"){
9514             return v;
9515         }
9516         if(v === undefined){
9517             return '';
9518         }
9519         if(typeof v == "number" || !El.unitPattern.test(v)){
9520             return v + (defaultUnit || 'px');
9521         }
9522         return v;
9523     };
9524
9525     // special markup used throughout Roo when box wrapping elements
9526     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9527     /**
9528      * Visibility mode constant - Use visibility to hide element
9529      * @static
9530      * @type Number
9531      */
9532     El.VISIBILITY = 1;
9533     /**
9534      * Visibility mode constant - Use display to hide element
9535      * @static
9536      * @type Number
9537      */
9538     El.DISPLAY = 2;
9539
9540     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9541     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9542     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9543
9544
9545
9546     /**
9547      * @private
9548      */
9549     El.cache = {};
9550
9551     var docEl;
9552
9553     /**
9554      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9555      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9556      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9557      * @return {Element} The Element object
9558      * @static
9559      */
9560     El.get = function(el){
9561         var ex, elm, id;
9562         if(!el){ return null; }
9563         if(typeof el == "string"){ // element id
9564             if(!(elm = document.getElementById(el))){
9565                 return null;
9566             }
9567             if(ex = El.cache[el]){
9568                 ex.dom = elm;
9569             }else{
9570                 ex = El.cache[el] = new El(elm);
9571             }
9572             return ex;
9573         }else if(el.tagName){ // dom element
9574             if(!(id = el.id)){
9575                 id = Roo.id(el);
9576             }
9577             if(ex = El.cache[id]){
9578                 ex.dom = el;
9579             }else{
9580                 ex = El.cache[id] = new El(el);
9581             }
9582             return ex;
9583         }else if(el instanceof El){
9584             if(el != docEl){
9585                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9586                                                               // catch case where it hasn't been appended
9587                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9588             }
9589             return el;
9590         }else if(el.isComposite){
9591             return el;
9592         }else if(el instanceof Array){
9593             return El.select(el);
9594         }else if(el == document){
9595             // create a bogus element object representing the document object
9596             if(!docEl){
9597                 var f = function(){};
9598                 f.prototype = El.prototype;
9599                 docEl = new f();
9600                 docEl.dom = document;
9601             }
9602             return docEl;
9603         }
9604         return null;
9605     };
9606
9607     // private
9608     El.uncache = function(el){
9609         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9610             if(a[i]){
9611                 delete El.cache[a[i].id || a[i]];
9612             }
9613         }
9614     };
9615
9616     // private
9617     // Garbage collection - uncache elements/purge listeners on orphaned elements
9618     // so we don't hold a reference and cause the browser to retain them
9619     El.garbageCollect = function(){
9620         if(!Roo.enableGarbageCollector){
9621             clearInterval(El.collectorThread);
9622             return;
9623         }
9624         for(var eid in El.cache){
9625             var el = El.cache[eid], d = el.dom;
9626             // -------------------------------------------------------
9627             // Determining what is garbage:
9628             // -------------------------------------------------------
9629             // !d
9630             // dom node is null, definitely garbage
9631             // -------------------------------------------------------
9632             // !d.parentNode
9633             // no parentNode == direct orphan, definitely garbage
9634             // -------------------------------------------------------
9635             // !d.offsetParent && !document.getElementById(eid)
9636             // display none elements have no offsetParent so we will
9637             // also try to look it up by it's id. However, check
9638             // offsetParent first so we don't do unneeded lookups.
9639             // This enables collection of elements that are not orphans
9640             // directly, but somewhere up the line they have an orphan
9641             // parent.
9642             // -------------------------------------------------------
9643             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9644                 delete El.cache[eid];
9645                 if(d && Roo.enableListenerCollection){
9646                     E.purgeElement(d);
9647                 }
9648             }
9649         }
9650     }
9651     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9652
9653
9654     // dom is optional
9655     El.Flyweight = function(dom){
9656         this.dom = dom;
9657     };
9658     El.Flyweight.prototype = El.prototype;
9659
9660     El._flyweights = {};
9661     /**
9662      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9663      * the dom node can be overwritten by other code.
9664      * @param {String/HTMLElement} el The dom node or id
9665      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9666      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9667      * @static
9668      * @return {Element} The shared Element object
9669      */
9670     El.fly = function(el, named){
9671         named = named || '_global';
9672         el = Roo.getDom(el);
9673         if(!el){
9674             return null;
9675         }
9676         if(!El._flyweights[named]){
9677             El._flyweights[named] = new El.Flyweight();
9678         }
9679         El._flyweights[named].dom = el;
9680         return El._flyweights[named];
9681     };
9682
9683     /**
9684      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9685      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9686      * Shorthand of {@link Roo.Element#get}
9687      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9688      * @return {Element} The Element object
9689      * @member Roo
9690      * @method get
9691      */
9692     Roo.get = El.get;
9693     /**
9694      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9695      * the dom node can be overwritten by other code.
9696      * Shorthand of {@link Roo.Element#fly}
9697      * @param {String/HTMLElement} el The dom node or id
9698      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9699      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9700      * @static
9701      * @return {Element} The shared Element object
9702      * @member Roo
9703      * @method fly
9704      */
9705     Roo.fly = El.fly;
9706
9707     // speedy lookup for elements never to box adjust
9708     var noBoxAdjust = Roo.isStrict ? {
9709         select:1
9710     } : {
9711         input:1, select:1, textarea:1
9712     };
9713     if(Roo.isIE || Roo.isGecko){
9714         noBoxAdjust['button'] = 1;
9715     }
9716
9717
9718     Roo.EventManager.on(window, 'unload', function(){
9719         delete El.cache;
9720         delete El._flyweights;
9721     });
9722 })();
9723
9724
9725
9726
9727 if(Roo.DomQuery){
9728     Roo.Element.selectorFunction = Roo.DomQuery.select;
9729 }
9730
9731 Roo.Element.select = function(selector, unique, root){
9732     var els;
9733     if(typeof selector == "string"){
9734         els = Roo.Element.selectorFunction(selector, root);
9735     }else if(selector.length !== undefined){
9736         els = selector;
9737     }else{
9738         throw "Invalid selector";
9739     }
9740     if(unique === true){
9741         return new Roo.CompositeElement(els);
9742     }else{
9743         return new Roo.CompositeElementLite(els);
9744     }
9745 };
9746 /**
9747  * Selects elements based on the passed CSS selector to enable working on them as 1.
9748  * @param {String/Array} selector The CSS selector or an array of elements
9749  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9750  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9751  * @return {CompositeElementLite/CompositeElement}
9752  * @member Roo
9753  * @method select
9754  */
9755 Roo.select = Roo.Element.select;
9756
9757
9758
9759
9760
9761
9762
9763
9764
9765
9766
9767
9768
9769
9770 /*
9771  * Based on:
9772  * Ext JS Library 1.1.1
9773  * Copyright(c) 2006-2007, Ext JS, LLC.
9774  *
9775  * Originally Released Under LGPL - original licence link has changed is not relivant.
9776  *
9777  * Fork - LGPL
9778  * <script type="text/javascript">
9779  */
9780
9781
9782
9783 //Notifies Element that fx methods are available
9784 Roo.enableFx = true;
9785
9786 /**
9787  * @class Roo.Fx
9788  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9789  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9790  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9791  * Element effects to work.</p><br/>
9792  *
9793  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9794  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9795  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9796  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9797  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9798  * expected results and should be done with care.</p><br/>
9799  *
9800  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9801  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9802 <pre>
9803 Value  Description
9804 -----  -----------------------------
9805 tl     The top left corner
9806 t      The center of the top edge
9807 tr     The top right corner
9808 l      The center of the left edge
9809 r      The center of the right edge
9810 bl     The bottom left corner
9811 b      The center of the bottom edge
9812 br     The bottom right corner
9813 </pre>
9814  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9815  * below are common options that can be passed to any Fx method.</b>
9816  * @cfg {Function} callback A function called when the effect is finished
9817  * @cfg {Object} scope The scope of the effect function
9818  * @cfg {String} easing A valid Easing value for the effect
9819  * @cfg {String} afterCls A css class to apply after the effect
9820  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9821  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9822  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9823  * effects that end with the element being visually hidden, ignored otherwise)
9824  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9825  * a function which returns such a specification that will be applied to the Element after the effect finishes
9826  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9827  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9828  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9829  */
9830 Roo.Fx = {
9831         /**
9832          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9833          * origin for the slide effect.  This function automatically handles wrapping the element with
9834          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9835          * Usage:
9836          *<pre><code>
9837 // default: slide the element in from the top
9838 el.slideIn();
9839
9840 // custom: slide the element in from the right with a 2-second duration
9841 el.slideIn('r', { duration: 2 });
9842
9843 // common config options shown with default values
9844 el.slideIn('t', {
9845     easing: 'easeOut',
9846     duration: .5
9847 });
9848 </code></pre>
9849          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9850          * @param {Object} options (optional) Object literal with any of the Fx config options
9851          * @return {Roo.Element} The Element
9852          */
9853     slideIn : function(anchor, o){
9854         var el = this.getFxEl();
9855         o = o || {};
9856
9857         el.queueFx(o, function(){
9858
9859             anchor = anchor || "t";
9860
9861             // fix display to visibility
9862             this.fixDisplay();
9863
9864             // restore values after effect
9865             var r = this.getFxRestore();
9866             var b = this.getBox();
9867             // fixed size for slide
9868             this.setSize(b);
9869
9870             // wrap if needed
9871             var wrap = this.fxWrap(r.pos, o, "hidden");
9872
9873             var st = this.dom.style;
9874             st.visibility = "visible";
9875             st.position = "absolute";
9876
9877             // clear out temp styles after slide and unwrap
9878             var after = function(){
9879                 el.fxUnwrap(wrap, r.pos, o);
9880                 st.width = r.width;
9881                 st.height = r.height;
9882                 el.afterFx(o);
9883             };
9884             // time to calc the positions
9885             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9886
9887             switch(anchor.toLowerCase()){
9888                 case "t":
9889                     wrap.setSize(b.width, 0);
9890                     st.left = st.bottom = "0";
9891                     a = {height: bh};
9892                 break;
9893                 case "l":
9894                     wrap.setSize(0, b.height);
9895                     st.right = st.top = "0";
9896                     a = {width: bw};
9897                 break;
9898                 case "r":
9899                     wrap.setSize(0, b.height);
9900                     wrap.setX(b.right);
9901                     st.left = st.top = "0";
9902                     a = {width: bw, points: pt};
9903                 break;
9904                 case "b":
9905                     wrap.setSize(b.width, 0);
9906                     wrap.setY(b.bottom);
9907                     st.left = st.top = "0";
9908                     a = {height: bh, points: pt};
9909                 break;
9910                 case "tl":
9911                     wrap.setSize(0, 0);
9912                     st.right = st.bottom = "0";
9913                     a = {width: bw, height: bh};
9914                 break;
9915                 case "bl":
9916                     wrap.setSize(0, 0);
9917                     wrap.setY(b.y+b.height);
9918                     st.right = st.top = "0";
9919                     a = {width: bw, height: bh, points: pt};
9920                 break;
9921                 case "br":
9922                     wrap.setSize(0, 0);
9923                     wrap.setXY([b.right, b.bottom]);
9924                     st.left = st.top = "0";
9925                     a = {width: bw, height: bh, points: pt};
9926                 break;
9927                 case "tr":
9928                     wrap.setSize(0, 0);
9929                     wrap.setX(b.x+b.width);
9930                     st.left = st.bottom = "0";
9931                     a = {width: bw, height: bh, points: pt};
9932                 break;
9933             }
9934             this.dom.style.visibility = "visible";
9935             wrap.show();
9936
9937             arguments.callee.anim = wrap.fxanim(a,
9938                 o,
9939                 'motion',
9940                 .5,
9941                 'easeOut', after);
9942         });
9943         return this;
9944     },
9945     
9946         /**
9947          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9948          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9949          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9950          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9951          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9952          * Usage:
9953          *<pre><code>
9954 // default: slide the element out to the top
9955 el.slideOut();
9956
9957 // custom: slide the element out to the right with a 2-second duration
9958 el.slideOut('r', { duration: 2 });
9959
9960 // common config options shown with default values
9961 el.slideOut('t', {
9962     easing: 'easeOut',
9963     duration: .5,
9964     remove: false,
9965     useDisplay: false
9966 });
9967 </code></pre>
9968          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9969          * @param {Object} options (optional) Object literal with any of the Fx config options
9970          * @return {Roo.Element} The Element
9971          */
9972     slideOut : function(anchor, o){
9973         var el = this.getFxEl();
9974         o = o || {};
9975
9976         el.queueFx(o, function(){
9977
9978             anchor = anchor || "t";
9979
9980             // restore values after effect
9981             var r = this.getFxRestore();
9982             
9983             var b = this.getBox();
9984             // fixed size for slide
9985             this.setSize(b);
9986
9987             // wrap if needed
9988             var wrap = this.fxWrap(r.pos, o, "visible");
9989
9990             var st = this.dom.style;
9991             st.visibility = "visible";
9992             st.position = "absolute";
9993
9994             wrap.setSize(b);
9995
9996             var after = function(){
9997                 if(o.useDisplay){
9998                     el.setDisplayed(false);
9999                 }else{
10000                     el.hide();
10001                 }
10002
10003                 el.fxUnwrap(wrap, r.pos, o);
10004
10005                 st.width = r.width;
10006                 st.height = r.height;
10007
10008                 el.afterFx(o);
10009             };
10010
10011             var a, zero = {to: 0};
10012             switch(anchor.toLowerCase()){
10013                 case "t":
10014                     st.left = st.bottom = "0";
10015                     a = {height: zero};
10016                 break;
10017                 case "l":
10018                     st.right = st.top = "0";
10019                     a = {width: zero};
10020                 break;
10021                 case "r":
10022                     st.left = st.top = "0";
10023                     a = {width: zero, points: {to:[b.right, b.y]}};
10024                 break;
10025                 case "b":
10026                     st.left = st.top = "0";
10027                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10028                 break;
10029                 case "tl":
10030                     st.right = st.bottom = "0";
10031                     a = {width: zero, height: zero};
10032                 break;
10033                 case "bl":
10034                     st.right = st.top = "0";
10035                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10036                 break;
10037                 case "br":
10038                     st.left = st.top = "0";
10039                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10040                 break;
10041                 case "tr":
10042                     st.left = st.bottom = "0";
10043                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10044                 break;
10045             }
10046
10047             arguments.callee.anim = wrap.fxanim(a,
10048                 o,
10049                 'motion',
10050                 .5,
10051                 "easeOut", after);
10052         });
10053         return this;
10054     },
10055
10056         /**
10057          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10058          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10059          * The element must be removed from the DOM using the 'remove' config option if desired.
10060          * Usage:
10061          *<pre><code>
10062 // default
10063 el.puff();
10064
10065 // common config options shown with default values
10066 el.puff({
10067     easing: 'easeOut',
10068     duration: .5,
10069     remove: false,
10070     useDisplay: false
10071 });
10072 </code></pre>
10073          * @param {Object} options (optional) Object literal with any of the Fx config options
10074          * @return {Roo.Element} The Element
10075          */
10076     puff : function(o){
10077         var el = this.getFxEl();
10078         o = o || {};
10079
10080         el.queueFx(o, function(){
10081             this.clearOpacity();
10082             this.show();
10083
10084             // restore values after effect
10085             var r = this.getFxRestore();
10086             var st = this.dom.style;
10087
10088             var after = function(){
10089                 if(o.useDisplay){
10090                     el.setDisplayed(false);
10091                 }else{
10092                     el.hide();
10093                 }
10094
10095                 el.clearOpacity();
10096
10097                 el.setPositioning(r.pos);
10098                 st.width = r.width;
10099                 st.height = r.height;
10100                 st.fontSize = '';
10101                 el.afterFx(o);
10102             };
10103
10104             var width = this.getWidth();
10105             var height = this.getHeight();
10106
10107             arguments.callee.anim = this.fxanim({
10108                     width : {to: this.adjustWidth(width * 2)},
10109                     height : {to: this.adjustHeight(height * 2)},
10110                     points : {by: [-(width * .5), -(height * .5)]},
10111                     opacity : {to: 0},
10112                     fontSize: {to:200, unit: "%"}
10113                 },
10114                 o,
10115                 'motion',
10116                 .5,
10117                 "easeOut", after);
10118         });
10119         return this;
10120     },
10121
10122         /**
10123          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10124          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10125          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10126          * Usage:
10127          *<pre><code>
10128 // default
10129 el.switchOff();
10130
10131 // all config options shown with default values
10132 el.switchOff({
10133     easing: 'easeIn',
10134     duration: .3,
10135     remove: false,
10136     useDisplay: false
10137 });
10138 </code></pre>
10139          * @param {Object} options (optional) Object literal with any of the Fx config options
10140          * @return {Roo.Element} The Element
10141          */
10142     switchOff : function(o){
10143         var el = this.getFxEl();
10144         o = o || {};
10145
10146         el.queueFx(o, function(){
10147             this.clearOpacity();
10148             this.clip();
10149
10150             // restore values after effect
10151             var r = this.getFxRestore();
10152             var st = this.dom.style;
10153
10154             var after = function(){
10155                 if(o.useDisplay){
10156                     el.setDisplayed(false);
10157                 }else{
10158                     el.hide();
10159                 }
10160
10161                 el.clearOpacity();
10162                 el.setPositioning(r.pos);
10163                 st.width = r.width;
10164                 st.height = r.height;
10165
10166                 el.afterFx(o);
10167             };
10168
10169             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10170                 this.clearOpacity();
10171                 (function(){
10172                     this.fxanim({
10173                         height:{to:1},
10174                         points:{by:[0, this.getHeight() * .5]}
10175                     }, o, 'motion', 0.3, 'easeIn', after);
10176                 }).defer(100, this);
10177             });
10178         });
10179         return this;
10180     },
10181
10182     /**
10183      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10184      * changed using the "attr" config option) and then fading back to the original color. If no original
10185      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10186      * Usage:
10187 <pre><code>
10188 // default: highlight background to yellow
10189 el.highlight();
10190
10191 // custom: highlight foreground text to blue for 2 seconds
10192 el.highlight("0000ff", { attr: 'color', duration: 2 });
10193
10194 // common config options shown with default values
10195 el.highlight("ffff9c", {
10196     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10197     endColor: (current color) or "ffffff",
10198     easing: 'easeIn',
10199     duration: 1
10200 });
10201 </code></pre>
10202      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10203      * @param {Object} options (optional) Object literal with any of the Fx config options
10204      * @return {Roo.Element} The Element
10205      */ 
10206     highlight : function(color, o){
10207         var el = this.getFxEl();
10208         o = o || {};
10209
10210         el.queueFx(o, function(){
10211             color = color || "ffff9c";
10212             attr = o.attr || "backgroundColor";
10213
10214             this.clearOpacity();
10215             this.show();
10216
10217             var origColor = this.getColor(attr);
10218             var restoreColor = this.dom.style[attr];
10219             endColor = (o.endColor || origColor) || "ffffff";
10220
10221             var after = function(){
10222                 el.dom.style[attr] = restoreColor;
10223                 el.afterFx(o);
10224             };
10225
10226             var a = {};
10227             a[attr] = {from: color, to: endColor};
10228             arguments.callee.anim = this.fxanim(a,
10229                 o,
10230                 'color',
10231                 1,
10232                 'easeIn', after);
10233         });
10234         return this;
10235     },
10236
10237    /**
10238     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10239     * Usage:
10240 <pre><code>
10241 // default: a single light blue ripple
10242 el.frame();
10243
10244 // custom: 3 red ripples lasting 3 seconds total
10245 el.frame("ff0000", 3, { duration: 3 });
10246
10247 // common config options shown with default values
10248 el.frame("C3DAF9", 1, {
10249     duration: 1 //duration of entire animation (not each individual ripple)
10250     // Note: Easing is not configurable and will be ignored if included
10251 });
10252 </code></pre>
10253     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10254     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10255     * @param {Object} options (optional) Object literal with any of the Fx config options
10256     * @return {Roo.Element} The Element
10257     */
10258     frame : function(color, count, o){
10259         var el = this.getFxEl();
10260         o = o || {};
10261
10262         el.queueFx(o, function(){
10263             color = color || "#C3DAF9";
10264             if(color.length == 6){
10265                 color = "#" + color;
10266             }
10267             count = count || 1;
10268             duration = o.duration || 1;
10269             this.show();
10270
10271             var b = this.getBox();
10272             var animFn = function(){
10273                 var proxy = this.createProxy({
10274
10275                      style:{
10276                         visbility:"hidden",
10277                         position:"absolute",
10278                         "z-index":"35000", // yee haw
10279                         border:"0px solid " + color
10280                      }
10281                   });
10282                 var scale = Roo.isBorderBox ? 2 : 1;
10283                 proxy.animate({
10284                     top:{from:b.y, to:b.y - 20},
10285                     left:{from:b.x, to:b.x - 20},
10286                     borderWidth:{from:0, to:10},
10287                     opacity:{from:1, to:0},
10288                     height:{from:b.height, to:(b.height + (20*scale))},
10289                     width:{from:b.width, to:(b.width + (20*scale))}
10290                 }, duration, function(){
10291                     proxy.remove();
10292                 });
10293                 if(--count > 0){
10294                      animFn.defer((duration/2)*1000, this);
10295                 }else{
10296                     el.afterFx(o);
10297                 }
10298             };
10299             animFn.call(this);
10300         });
10301         return this;
10302     },
10303
10304    /**
10305     * Creates a pause before any subsequent queued effects begin.  If there are
10306     * no effects queued after the pause it will have no effect.
10307     * Usage:
10308 <pre><code>
10309 el.pause(1);
10310 </code></pre>
10311     * @param {Number} seconds The length of time to pause (in seconds)
10312     * @return {Roo.Element} The Element
10313     */
10314     pause : function(seconds){
10315         var el = this.getFxEl();
10316         var o = {};
10317
10318         el.queueFx(o, function(){
10319             setTimeout(function(){
10320                 el.afterFx(o);
10321             }, seconds * 1000);
10322         });
10323         return this;
10324     },
10325
10326    /**
10327     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10328     * using the "endOpacity" config option.
10329     * Usage:
10330 <pre><code>
10331 // default: fade in from opacity 0 to 100%
10332 el.fadeIn();
10333
10334 // custom: fade in from opacity 0 to 75% over 2 seconds
10335 el.fadeIn({ endOpacity: .75, duration: 2});
10336
10337 // common config options shown with default values
10338 el.fadeIn({
10339     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10340     easing: 'easeOut',
10341     duration: .5
10342 });
10343 </code></pre>
10344     * @param {Object} options (optional) Object literal with any of the Fx config options
10345     * @return {Roo.Element} The Element
10346     */
10347     fadeIn : function(o){
10348         var el = this.getFxEl();
10349         o = o || {};
10350         el.queueFx(o, function(){
10351             this.setOpacity(0);
10352             this.fixDisplay();
10353             this.dom.style.visibility = 'visible';
10354             var to = o.endOpacity || 1;
10355             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10356                 o, null, .5, "easeOut", function(){
10357                 if(to == 1){
10358                     this.clearOpacity();
10359                 }
10360                 el.afterFx(o);
10361             });
10362         });
10363         return this;
10364     },
10365
10366    /**
10367     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10368     * using the "endOpacity" config option.
10369     * Usage:
10370 <pre><code>
10371 // default: fade out from the element's current opacity to 0
10372 el.fadeOut();
10373
10374 // custom: fade out from the element's current opacity to 25% over 2 seconds
10375 el.fadeOut({ endOpacity: .25, duration: 2});
10376
10377 // common config options shown with default values
10378 el.fadeOut({
10379     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10380     easing: 'easeOut',
10381     duration: .5
10382     remove: false,
10383     useDisplay: false
10384 });
10385 </code></pre>
10386     * @param {Object} options (optional) Object literal with any of the Fx config options
10387     * @return {Roo.Element} The Element
10388     */
10389     fadeOut : function(o){
10390         var el = this.getFxEl();
10391         o = o || {};
10392         el.queueFx(o, function(){
10393             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10394                 o, null, .5, "easeOut", function(){
10395                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10396                      this.dom.style.display = "none";
10397                 }else{
10398                      this.dom.style.visibility = "hidden";
10399                 }
10400                 this.clearOpacity();
10401                 el.afterFx(o);
10402             });
10403         });
10404         return this;
10405     },
10406
10407    /**
10408     * Animates the transition of an element's dimensions from a starting height/width
10409     * to an ending height/width.
10410     * Usage:
10411 <pre><code>
10412 // change height and width to 100x100 pixels
10413 el.scale(100, 100);
10414
10415 // common config options shown with default values.  The height and width will default to
10416 // the element's existing values if passed as null.
10417 el.scale(
10418     [element's width],
10419     [element's height], {
10420     easing: 'easeOut',
10421     duration: .35
10422 });
10423 </code></pre>
10424     * @param {Number} width  The new width (pass undefined to keep the original width)
10425     * @param {Number} height  The new height (pass undefined to keep the original height)
10426     * @param {Object} options (optional) Object literal with any of the Fx config options
10427     * @return {Roo.Element} The Element
10428     */
10429     scale : function(w, h, o){
10430         this.shift(Roo.apply({}, o, {
10431             width: w,
10432             height: h
10433         }));
10434         return this;
10435     },
10436
10437    /**
10438     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10439     * Any of these properties not specified in the config object will not be changed.  This effect 
10440     * requires that at least one new dimension, position or opacity setting must be passed in on
10441     * the config object in order for the function to have any effect.
10442     * Usage:
10443 <pre><code>
10444 // slide the element horizontally to x position 200 while changing the height and opacity
10445 el.shift({ x: 200, height: 50, opacity: .8 });
10446
10447 // common config options shown with default values.
10448 el.shift({
10449     width: [element's width],
10450     height: [element's height],
10451     x: [element's x position],
10452     y: [element's y position],
10453     opacity: [element's opacity],
10454     easing: 'easeOut',
10455     duration: .35
10456 });
10457 </code></pre>
10458     * @param {Object} options  Object literal with any of the Fx config options
10459     * @return {Roo.Element} The Element
10460     */
10461     shift : function(o){
10462         var el = this.getFxEl();
10463         o = o || {};
10464         el.queueFx(o, function(){
10465             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10466             if(w !== undefined){
10467                 a.width = {to: this.adjustWidth(w)};
10468             }
10469             if(h !== undefined){
10470                 a.height = {to: this.adjustHeight(h)};
10471             }
10472             if(x !== undefined || y !== undefined){
10473                 a.points = {to: [
10474                     x !== undefined ? x : this.getX(),
10475                     y !== undefined ? y : this.getY()
10476                 ]};
10477             }
10478             if(op !== undefined){
10479                 a.opacity = {to: op};
10480             }
10481             if(o.xy !== undefined){
10482                 a.points = {to: o.xy};
10483             }
10484             arguments.callee.anim = this.fxanim(a,
10485                 o, 'motion', .35, "easeOut", function(){
10486                 el.afterFx(o);
10487             });
10488         });
10489         return this;
10490     },
10491
10492         /**
10493          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10494          * ending point of the effect.
10495          * Usage:
10496          *<pre><code>
10497 // default: slide the element downward while fading out
10498 el.ghost();
10499
10500 // custom: slide the element out to the right with a 2-second duration
10501 el.ghost('r', { duration: 2 });
10502
10503 // common config options shown with default values
10504 el.ghost('b', {
10505     easing: 'easeOut',
10506     duration: .5
10507     remove: false,
10508     useDisplay: false
10509 });
10510 </code></pre>
10511          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10512          * @param {Object} options (optional) Object literal with any of the Fx config options
10513          * @return {Roo.Element} The Element
10514          */
10515     ghost : function(anchor, o){
10516         var el = this.getFxEl();
10517         o = o || {};
10518
10519         el.queueFx(o, function(){
10520             anchor = anchor || "b";
10521
10522             // restore values after effect
10523             var r = this.getFxRestore();
10524             var w = this.getWidth(),
10525                 h = this.getHeight();
10526
10527             var st = this.dom.style;
10528
10529             var after = function(){
10530                 if(o.useDisplay){
10531                     el.setDisplayed(false);
10532                 }else{
10533                     el.hide();
10534                 }
10535
10536                 el.clearOpacity();
10537                 el.setPositioning(r.pos);
10538                 st.width = r.width;
10539                 st.height = r.height;
10540
10541                 el.afterFx(o);
10542             };
10543
10544             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10545             switch(anchor.toLowerCase()){
10546                 case "t":
10547                     pt.by = [0, -h];
10548                 break;
10549                 case "l":
10550                     pt.by = [-w, 0];
10551                 break;
10552                 case "r":
10553                     pt.by = [w, 0];
10554                 break;
10555                 case "b":
10556                     pt.by = [0, h];
10557                 break;
10558                 case "tl":
10559                     pt.by = [-w, -h];
10560                 break;
10561                 case "bl":
10562                     pt.by = [-w, h];
10563                 break;
10564                 case "br":
10565                     pt.by = [w, h];
10566                 break;
10567                 case "tr":
10568                     pt.by = [w, -h];
10569                 break;
10570             }
10571
10572             arguments.callee.anim = this.fxanim(a,
10573                 o,
10574                 'motion',
10575                 .5,
10576                 "easeOut", after);
10577         });
10578         return this;
10579     },
10580
10581         /**
10582          * Ensures that all effects queued after syncFx is called on the element are
10583          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10584          * @return {Roo.Element} The Element
10585          */
10586     syncFx : function(){
10587         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10588             block : false,
10589             concurrent : true,
10590             stopFx : false
10591         });
10592         return this;
10593     },
10594
10595         /**
10596          * Ensures that all effects queued after sequenceFx is called on the element are
10597          * run in sequence.  This is the opposite of {@link #syncFx}.
10598          * @return {Roo.Element} The Element
10599          */
10600     sequenceFx : function(){
10601         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10602             block : false,
10603             concurrent : false,
10604             stopFx : false
10605         });
10606         return this;
10607     },
10608
10609         /* @private */
10610     nextFx : function(){
10611         var ef = this.fxQueue[0];
10612         if(ef){
10613             ef.call(this);
10614         }
10615     },
10616
10617         /**
10618          * Returns true if the element has any effects actively running or queued, else returns false.
10619          * @return {Boolean} True if element has active effects, else false
10620          */
10621     hasActiveFx : function(){
10622         return this.fxQueue && this.fxQueue[0];
10623     },
10624
10625         /**
10626          * Stops any running effects and clears the element's internal effects queue if it contains
10627          * any additional effects that haven't started yet.
10628          * @return {Roo.Element} The Element
10629          */
10630     stopFx : function(){
10631         if(this.hasActiveFx()){
10632             var cur = this.fxQueue[0];
10633             if(cur && cur.anim && cur.anim.isAnimated()){
10634                 this.fxQueue = [cur]; // clear out others
10635                 cur.anim.stop(true);
10636             }
10637         }
10638         return this;
10639     },
10640
10641         /* @private */
10642     beforeFx : function(o){
10643         if(this.hasActiveFx() && !o.concurrent){
10644            if(o.stopFx){
10645                this.stopFx();
10646                return true;
10647            }
10648            return false;
10649         }
10650         return true;
10651     },
10652
10653         /**
10654          * Returns true if the element is currently blocking so that no other effect can be queued
10655          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10656          * used to ensure that an effect initiated by a user action runs to completion prior to the
10657          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10658          * @return {Boolean} True if blocking, else false
10659          */
10660     hasFxBlock : function(){
10661         var q = this.fxQueue;
10662         return q && q[0] && q[0].block;
10663     },
10664
10665         /* @private */
10666     queueFx : function(o, fn){
10667         if(!this.fxQueue){
10668             this.fxQueue = [];
10669         }
10670         if(!this.hasFxBlock()){
10671             Roo.applyIf(o, this.fxDefaults);
10672             if(!o.concurrent){
10673                 var run = this.beforeFx(o);
10674                 fn.block = o.block;
10675                 this.fxQueue.push(fn);
10676                 if(run){
10677                     this.nextFx();
10678                 }
10679             }else{
10680                 fn.call(this);
10681             }
10682         }
10683         return this;
10684     },
10685
10686         /* @private */
10687     fxWrap : function(pos, o, vis){
10688         var wrap;
10689         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10690             var wrapXY;
10691             if(o.fixPosition){
10692                 wrapXY = this.getXY();
10693             }
10694             var div = document.createElement("div");
10695             div.style.visibility = vis;
10696             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10697             wrap.setPositioning(pos);
10698             if(wrap.getStyle("position") == "static"){
10699                 wrap.position("relative");
10700             }
10701             this.clearPositioning('auto');
10702             wrap.clip();
10703             wrap.dom.appendChild(this.dom);
10704             if(wrapXY){
10705                 wrap.setXY(wrapXY);
10706             }
10707         }
10708         return wrap;
10709     },
10710
10711         /* @private */
10712     fxUnwrap : function(wrap, pos, o){
10713         this.clearPositioning();
10714         this.setPositioning(pos);
10715         if(!o.wrap){
10716             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10717             wrap.remove();
10718         }
10719     },
10720
10721         /* @private */
10722     getFxRestore : function(){
10723         var st = this.dom.style;
10724         return {pos: this.getPositioning(), width: st.width, height : st.height};
10725     },
10726
10727         /* @private */
10728     afterFx : function(o){
10729         if(o.afterStyle){
10730             this.applyStyles(o.afterStyle);
10731         }
10732         if(o.afterCls){
10733             this.addClass(o.afterCls);
10734         }
10735         if(o.remove === true){
10736             this.remove();
10737         }
10738         Roo.callback(o.callback, o.scope, [this]);
10739         if(!o.concurrent){
10740             this.fxQueue.shift();
10741             this.nextFx();
10742         }
10743     },
10744
10745         /* @private */
10746     getFxEl : function(){ // support for composite element fx
10747         return Roo.get(this.dom);
10748     },
10749
10750         /* @private */
10751     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10752         animType = animType || 'run';
10753         opt = opt || {};
10754         var anim = Roo.lib.Anim[animType](
10755             this.dom, args,
10756             (opt.duration || defaultDur) || .35,
10757             (opt.easing || defaultEase) || 'easeOut',
10758             function(){
10759                 Roo.callback(cb, this);
10760             },
10761             this
10762         );
10763         opt.anim = anim;
10764         return anim;
10765     }
10766 };
10767
10768 // backwords compat
10769 Roo.Fx.resize = Roo.Fx.scale;
10770
10771 //When included, Roo.Fx is automatically applied to Element so that all basic
10772 //effects are available directly via the Element API
10773 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10774  * Based on:
10775  * Ext JS Library 1.1.1
10776  * Copyright(c) 2006-2007, Ext JS, LLC.
10777  *
10778  * Originally Released Under LGPL - original licence link has changed is not relivant.
10779  *
10780  * Fork - LGPL
10781  * <script type="text/javascript">
10782  */
10783
10784
10785 /**
10786  * @class Roo.CompositeElement
10787  * Standard composite class. Creates a Roo.Element for every element in the collection.
10788  * <br><br>
10789  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10790  * actions will be performed on all the elements in this collection.</b>
10791  * <br><br>
10792  * All methods return <i>this</i> and can be chained.
10793  <pre><code>
10794  var els = Roo.select("#some-el div.some-class", true);
10795  // or select directly from an existing element
10796  var el = Roo.get('some-el');
10797  el.select('div.some-class', true);
10798
10799  els.setWidth(100); // all elements become 100 width
10800  els.hide(true); // all elements fade out and hide
10801  // or
10802  els.setWidth(100).hide(true);
10803  </code></pre>
10804  */
10805 Roo.CompositeElement = function(els){
10806     this.elements = [];
10807     this.addElements(els);
10808 };
10809 Roo.CompositeElement.prototype = {
10810     isComposite: true,
10811     addElements : function(els){
10812         if(!els) return this;
10813         if(typeof els == "string"){
10814             els = Roo.Element.selectorFunction(els);
10815         }
10816         var yels = this.elements;
10817         var index = yels.length-1;
10818         for(var i = 0, len = els.length; i < len; i++) {
10819                 yels[++index] = Roo.get(els[i]);
10820         }
10821         return this;
10822     },
10823
10824     /**
10825     * Clears this composite and adds the elements returned by the passed selector.
10826     * @param {String/Array} els A string CSS selector, an array of elements or an element
10827     * @return {CompositeElement} this
10828     */
10829     fill : function(els){
10830         this.elements = [];
10831         this.add(els);
10832         return this;
10833     },
10834
10835     /**
10836     * Filters this composite to only elements that match the passed selector.
10837     * @param {String} selector A string CSS selector
10838     * @return {CompositeElement} this
10839     */
10840     filter : function(selector){
10841         var els = [];
10842         this.each(function(el){
10843             if(el.is(selector)){
10844                 els[els.length] = el.dom;
10845             }
10846         });
10847         this.fill(els);
10848         return this;
10849     },
10850
10851     invoke : function(fn, args){
10852         var els = this.elements;
10853         for(var i = 0, len = els.length; i < len; i++) {
10854                 Roo.Element.prototype[fn].apply(els[i], args);
10855         }
10856         return this;
10857     },
10858     /**
10859     * Adds elements to this composite.
10860     * @param {String/Array} els A string CSS selector, an array of elements or an element
10861     * @return {CompositeElement} this
10862     */
10863     add : function(els){
10864         if(typeof els == "string"){
10865             this.addElements(Roo.Element.selectorFunction(els));
10866         }else if(els.length !== undefined){
10867             this.addElements(els);
10868         }else{
10869             this.addElements([els]);
10870         }
10871         return this;
10872     },
10873     /**
10874     * Calls the passed function passing (el, this, index) for each element in this composite.
10875     * @param {Function} fn The function to call
10876     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10877     * @return {CompositeElement} this
10878     */
10879     each : function(fn, scope){
10880         var els = this.elements;
10881         for(var i = 0, len = els.length; i < len; i++){
10882             if(fn.call(scope || els[i], els[i], this, i) === false) {
10883                 break;
10884             }
10885         }
10886         return this;
10887     },
10888
10889     /**
10890      * Returns the Element object at the specified index
10891      * @param {Number} index
10892      * @return {Roo.Element}
10893      */
10894     item : function(index){
10895         return this.elements[index] || null;
10896     },
10897
10898     /**
10899      * Returns the first Element
10900      * @return {Roo.Element}
10901      */
10902     first : function(){
10903         return this.item(0);
10904     },
10905
10906     /**
10907      * Returns the last Element
10908      * @return {Roo.Element}
10909      */
10910     last : function(){
10911         return this.item(this.elements.length-1);
10912     },
10913
10914     /**
10915      * Returns the number of elements in this composite
10916      * @return Number
10917      */
10918     getCount : function(){
10919         return this.elements.length;
10920     },
10921
10922     /**
10923      * Returns true if this composite contains the passed element
10924      * @return Boolean
10925      */
10926     contains : function(el){
10927         return this.indexOf(el) !== -1;
10928     },
10929
10930     /**
10931      * Returns true if this composite contains the passed element
10932      * @return Boolean
10933      */
10934     indexOf : function(el){
10935         return this.elements.indexOf(Roo.get(el));
10936     },
10937
10938
10939     /**
10940     * Removes the specified element(s).
10941     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10942     * or an array of any of those.
10943     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10944     * @return {CompositeElement} this
10945     */
10946     removeElement : function(el, removeDom){
10947         if(el instanceof Array){
10948             for(var i = 0, len = el.length; i < len; i++){
10949                 this.removeElement(el[i]);
10950             }
10951             return this;
10952         }
10953         var index = typeof el == 'number' ? el : this.indexOf(el);
10954         if(index !== -1){
10955             if(removeDom){
10956                 var d = this.elements[index];
10957                 if(d.dom){
10958                     d.remove();
10959                 }else{
10960                     d.parentNode.removeChild(d);
10961                 }
10962             }
10963             this.elements.splice(index, 1);
10964         }
10965         return this;
10966     },
10967
10968     /**
10969     * Replaces the specified element with the passed element.
10970     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10971     * to replace.
10972     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10973     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10974     * @return {CompositeElement} this
10975     */
10976     replaceElement : function(el, replacement, domReplace){
10977         var index = typeof el == 'number' ? el : this.indexOf(el);
10978         if(index !== -1){
10979             if(domReplace){
10980                 this.elements[index].replaceWith(replacement);
10981             }else{
10982                 this.elements.splice(index, 1, Roo.get(replacement))
10983             }
10984         }
10985         return this;
10986     },
10987
10988     /**
10989      * Removes all elements.
10990      */
10991     clear : function(){
10992         this.elements = [];
10993     }
10994 };
10995 (function(){
10996     Roo.CompositeElement.createCall = function(proto, fnName){
10997         if(!proto[fnName]){
10998             proto[fnName] = function(){
10999                 return this.invoke(fnName, arguments);
11000             };
11001         }
11002     };
11003     for(var fnName in Roo.Element.prototype){
11004         if(typeof Roo.Element.prototype[fnName] == "function"){
11005             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11006         }
11007     };
11008 })();
11009 /*
11010  * Based on:
11011  * Ext JS Library 1.1.1
11012  * Copyright(c) 2006-2007, Ext JS, LLC.
11013  *
11014  * Originally Released Under LGPL - original licence link has changed is not relivant.
11015  *
11016  * Fork - LGPL
11017  * <script type="text/javascript">
11018  */
11019
11020 /**
11021  * @class Roo.CompositeElementLite
11022  * @extends Roo.CompositeElement
11023  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11024  <pre><code>
11025  var els = Roo.select("#some-el div.some-class");
11026  // or select directly from an existing element
11027  var el = Roo.get('some-el');
11028  el.select('div.some-class');
11029
11030  els.setWidth(100); // all elements become 100 width
11031  els.hide(true); // all elements fade out and hide
11032  // or
11033  els.setWidth(100).hide(true);
11034  </code></pre><br><br>
11035  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11036  * actions will be performed on all the elements in this collection.</b>
11037  */
11038 Roo.CompositeElementLite = function(els){
11039     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11040     this.el = new Roo.Element.Flyweight();
11041 };
11042 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11043     addElements : function(els){
11044         if(els){
11045             if(els instanceof Array){
11046                 this.elements = this.elements.concat(els);
11047             }else{
11048                 var yels = this.elements;
11049                 var index = yels.length-1;
11050                 for(var i = 0, len = els.length; i < len; i++) {
11051                     yels[++index] = els[i];
11052                 }
11053             }
11054         }
11055         return this;
11056     },
11057     invoke : function(fn, args){
11058         var els = this.elements;
11059         var el = this.el;
11060         for(var i = 0, len = els.length; i < len; i++) {
11061             el.dom = els[i];
11062                 Roo.Element.prototype[fn].apply(el, args);
11063         }
11064         return this;
11065     },
11066     /**
11067      * Returns a flyweight Element of the dom element object at the specified index
11068      * @param {Number} index
11069      * @return {Roo.Element}
11070      */
11071     item : function(index){
11072         if(!this.elements[index]){
11073             return null;
11074         }
11075         this.el.dom = this.elements[index];
11076         return this.el;
11077     },
11078
11079     // fixes scope with flyweight
11080     addListener : function(eventName, handler, scope, opt){
11081         var els = this.elements;
11082         for(var i = 0, len = els.length; i < len; i++) {
11083             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11084         }
11085         return this;
11086     },
11087
11088     /**
11089     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11090     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11091     * a reference to the dom node, use el.dom.</b>
11092     * @param {Function} fn The function to call
11093     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11094     * @return {CompositeElement} this
11095     */
11096     each : function(fn, scope){
11097         var els = this.elements;
11098         var el = this.el;
11099         for(var i = 0, len = els.length; i < len; i++){
11100             el.dom = els[i];
11101                 if(fn.call(scope || el, el, this, i) === false){
11102                 break;
11103             }
11104         }
11105         return this;
11106     },
11107
11108     indexOf : function(el){
11109         return this.elements.indexOf(Roo.getDom(el));
11110     },
11111
11112     replaceElement : function(el, replacement, domReplace){
11113         var index = typeof el == 'number' ? el : this.indexOf(el);
11114         if(index !== -1){
11115             replacement = Roo.getDom(replacement);
11116             if(domReplace){
11117                 var d = this.elements[index];
11118                 d.parentNode.insertBefore(replacement, d);
11119                 d.parentNode.removeChild(d);
11120             }
11121             this.elements.splice(index, 1, replacement);
11122         }
11123         return this;
11124     }
11125 });
11126 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11127
11128 /*
11129  * Based on:
11130  * Ext JS Library 1.1.1
11131  * Copyright(c) 2006-2007, Ext JS, LLC.
11132  *
11133  * Originally Released Under LGPL - original licence link has changed is not relivant.
11134  *
11135  * Fork - LGPL
11136  * <script type="text/javascript">
11137  */
11138
11139  
11140
11141 /**
11142  * @class Roo.data.Connection
11143  * @extends Roo.util.Observable
11144  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11145  * either to a configured URL, or to a URL specified at request time.<br><br>
11146  * <p>
11147  * Requests made by this class are asynchronous, and will return immediately. No data from
11148  * the server will be available to the statement immediately following the {@link #request} call.
11149  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11150  * <p>
11151  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11152  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11153  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11154  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11155  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11156  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11157  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11158  * standard DOM methods.
11159  * @constructor
11160  * @param {Object} config a configuration object.
11161  */
11162 Roo.data.Connection = function(config){
11163     Roo.apply(this, config);
11164     this.addEvents({
11165         /**
11166          * @event beforerequest
11167          * Fires before a network request is made to retrieve a data object.
11168          * @param {Connection} conn This Connection object.
11169          * @param {Object} options The options config object passed to the {@link #request} method.
11170          */
11171         "beforerequest" : true,
11172         /**
11173          * @event requestcomplete
11174          * Fires if the request was successfully completed.
11175          * @param {Connection} conn This Connection object.
11176          * @param {Object} response The XHR object containing the response data.
11177          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11178          * @param {Object} options The options config object passed to the {@link #request} method.
11179          */
11180         "requestcomplete" : true,
11181         /**
11182          * @event requestexception
11183          * Fires if an error HTTP status was returned from the server.
11184          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11185          * @param {Connection} conn This Connection object.
11186          * @param {Object} response The XHR object containing the response data.
11187          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11188          * @param {Object} options The options config object passed to the {@link #request} method.
11189          */
11190         "requestexception" : true
11191     });
11192     Roo.data.Connection.superclass.constructor.call(this);
11193 };
11194
11195 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11196     /**
11197      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11198      */
11199     /**
11200      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11201      * extra parameters to each request made by this object. (defaults to undefined)
11202      */
11203     /**
11204      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11205      *  to each request made by this object. (defaults to undefined)
11206      */
11207     /**
11208      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11209      */
11210     /**
11211      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11212      */
11213     timeout : 30000,
11214     /**
11215      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11216      * @type Boolean
11217      */
11218     autoAbort:false,
11219
11220     /**
11221      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11222      * @type Boolean
11223      */
11224     disableCaching: true,
11225
11226     /**
11227      * Sends an HTTP request to a remote server.
11228      * @param {Object} options An object which may contain the following properties:<ul>
11229      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11230      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11231      * request, a url encoded string or a function to call to get either.</li>
11232      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11233      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11234      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11235      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11236      * <li>options {Object} The parameter to the request call.</li>
11237      * <li>success {Boolean} True if the request succeeded.</li>
11238      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11239      * </ul></li>
11240      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11241      * The callback is passed the following parameters:<ul>
11242      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11243      * <li>options {Object} The parameter to the request call.</li>
11244      * </ul></li>
11245      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11246      * The callback is passed the following parameters:<ul>
11247      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11248      * <li>options {Object} The parameter to the request call.</li>
11249      * </ul></li>
11250      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11251      * for the callback function. Defaults to the browser window.</li>
11252      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11253      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11254      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11255      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11256      * params for the post data. Any params will be appended to the URL.</li>
11257      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11258      * </ul>
11259      * @return {Number} transactionId
11260      */
11261     request : function(o){
11262         if(this.fireEvent("beforerequest", this, o) !== false){
11263             var p = o.params;
11264
11265             if(typeof p == "function"){
11266                 p = p.call(o.scope||window, o);
11267             }
11268             if(typeof p == "object"){
11269                 p = Roo.urlEncode(o.params);
11270             }
11271             if(this.extraParams){
11272                 var extras = Roo.urlEncode(this.extraParams);
11273                 p = p ? (p + '&' + extras) : extras;
11274             }
11275
11276             var url = o.url || this.url;
11277             if(typeof url == 'function'){
11278                 url = url.call(o.scope||window, o);
11279             }
11280
11281             if(o.form){
11282                 var form = Roo.getDom(o.form);
11283                 url = url || form.action;
11284
11285                 var enctype = form.getAttribute("enctype");
11286                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11287                     return this.doFormUpload(o, p, url);
11288                 }
11289                 var f = Roo.lib.Ajax.serializeForm(form);
11290                 p = p ? (p + '&' + f) : f;
11291             }
11292
11293             var hs = o.headers;
11294             if(this.defaultHeaders){
11295                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11296                 if(!o.headers){
11297                     o.headers = hs;
11298                 }
11299             }
11300
11301             var cb = {
11302                 success: this.handleResponse,
11303                 failure: this.handleFailure,
11304                 scope: this,
11305                 argument: {options: o},
11306                 timeout : this.timeout
11307             };
11308
11309             var method = o.method||this.method||(p ? "POST" : "GET");
11310
11311             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11312                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11313             }
11314
11315             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11316                 if(o.autoAbort){
11317                     this.abort();
11318                 }
11319             }else if(this.autoAbort !== false){
11320                 this.abort();
11321             }
11322
11323             if((method == 'GET' && p) || o.xmlData){
11324                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11325                 p = '';
11326             }
11327             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11328             return this.transId;
11329         }else{
11330             Roo.callback(o.callback, o.scope, [o, null, null]);
11331             return null;
11332         }
11333     },
11334
11335     /**
11336      * Determine whether this object has a request outstanding.
11337      * @param {Number} transactionId (Optional) defaults to the last transaction
11338      * @return {Boolean} True if there is an outstanding request.
11339      */
11340     isLoading : function(transId){
11341         if(transId){
11342             return Roo.lib.Ajax.isCallInProgress(transId);
11343         }else{
11344             return this.transId ? true : false;
11345         }
11346     },
11347
11348     /**
11349      * Aborts any outstanding request.
11350      * @param {Number} transactionId (Optional) defaults to the last transaction
11351      */
11352     abort : function(transId){
11353         if(transId || this.isLoading()){
11354             Roo.lib.Ajax.abort(transId || this.transId);
11355         }
11356     },
11357
11358     // private
11359     handleResponse : function(response){
11360         this.transId = false;
11361         var options = response.argument.options;
11362         response.argument = options ? options.argument : null;
11363         this.fireEvent("requestcomplete", this, response, options);
11364         Roo.callback(options.success, options.scope, [response, options]);
11365         Roo.callback(options.callback, options.scope, [options, true, response]);
11366     },
11367
11368     // private
11369     handleFailure : function(response, e){
11370         this.transId = false;
11371         var options = response.argument.options;
11372         response.argument = options ? options.argument : null;
11373         this.fireEvent("requestexception", this, response, options, e);
11374         Roo.callback(options.failure, options.scope, [response, options]);
11375         Roo.callback(options.callback, options.scope, [options, false, response]);
11376     },
11377
11378     // private
11379     doFormUpload : function(o, ps, url){
11380         var id = Roo.id();
11381         var frame = document.createElement('iframe');
11382         frame.id = id;
11383         frame.name = id;
11384         frame.className = 'x-hidden';
11385         if(Roo.isIE){
11386             frame.src = Roo.SSL_SECURE_URL;
11387         }
11388         document.body.appendChild(frame);
11389
11390         if(Roo.isIE){
11391            document.frames[id].name = id;
11392         }
11393
11394         var form = Roo.getDom(o.form);
11395         form.target = id;
11396         form.method = 'POST';
11397         form.enctype = form.encoding = 'multipart/form-data';
11398         if(url){
11399             form.action = url;
11400         }
11401
11402         var hiddens, hd;
11403         if(ps){ // add dynamic params
11404             hiddens = [];
11405             ps = Roo.urlDecode(ps, false);
11406             for(var k in ps){
11407                 if(ps.hasOwnProperty(k)){
11408                     hd = document.createElement('input');
11409                     hd.type = 'hidden';
11410                     hd.name = k;
11411                     hd.value = ps[k];
11412                     form.appendChild(hd);
11413                     hiddens.push(hd);
11414                 }
11415             }
11416         }
11417
11418         function cb(){
11419             var r = {  // bogus response object
11420                 responseText : '',
11421                 responseXML : null
11422             };
11423
11424             r.argument = o ? o.argument : null;
11425
11426             try { //
11427                 var doc;
11428                 if(Roo.isIE){
11429                     doc = frame.contentWindow.document;
11430                 }else {
11431                     doc = (frame.contentDocument || window.frames[id].document);
11432                 }
11433                 if(doc && doc.body){
11434                     r.responseText = doc.body.innerHTML;
11435                 }
11436                 if(doc && doc.XMLDocument){
11437                     r.responseXML = doc.XMLDocument;
11438                 }else {
11439                     r.responseXML = doc;
11440                 }
11441             }
11442             catch(e) {
11443                 // ignore
11444             }
11445
11446             Roo.EventManager.removeListener(frame, 'load', cb, this);
11447
11448             this.fireEvent("requestcomplete", this, r, o);
11449             Roo.callback(o.success, o.scope, [r, o]);
11450             Roo.callback(o.callback, o.scope, [o, true, r]);
11451
11452             setTimeout(function(){document.body.removeChild(frame);}, 100);
11453         }
11454
11455         Roo.EventManager.on(frame, 'load', cb, this);
11456         form.submit();
11457
11458         if(hiddens){ // remove dynamic params
11459             for(var i = 0, len = hiddens.length; i < len; i++){
11460                 form.removeChild(hiddens[i]);
11461             }
11462         }
11463     }
11464 });
11465
11466 /**
11467  * @class Roo.Ajax
11468  * @extends Roo.data.Connection
11469  * Global Ajax request class.
11470  *
11471  * @singleton
11472  */
11473 Roo.Ajax = new Roo.data.Connection({
11474     // fix up the docs
11475    /**
11476      * @cfg {String} url @hide
11477      */
11478     /**
11479      * @cfg {Object} extraParams @hide
11480      */
11481     /**
11482      * @cfg {Object} defaultHeaders @hide
11483      */
11484     /**
11485      * @cfg {String} method (Optional) @hide
11486      */
11487     /**
11488      * @cfg {Number} timeout (Optional) @hide
11489      */
11490     /**
11491      * @cfg {Boolean} autoAbort (Optional) @hide
11492      */
11493
11494     /**
11495      * @cfg {Boolean} disableCaching (Optional) @hide
11496      */
11497
11498     /**
11499      * @property  disableCaching
11500      * True to add a unique cache-buster param to GET requests. (defaults to true)
11501      * @type Boolean
11502      */
11503     /**
11504      * @property  url
11505      * The default URL to be used for requests to the server. (defaults to undefined)
11506      * @type String
11507      */
11508     /**
11509      * @property  extraParams
11510      * An object containing properties which are used as
11511      * extra parameters to each request made by this object. (defaults to undefined)
11512      * @type Object
11513      */
11514     /**
11515      * @property  defaultHeaders
11516      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11517      * @type Object
11518      */
11519     /**
11520      * @property  method
11521      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11522      * @type String
11523      */
11524     /**
11525      * @property  timeout
11526      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11527      * @type Number
11528      */
11529
11530     /**
11531      * @property  autoAbort
11532      * Whether a new request should abort any pending requests. (defaults to false)
11533      * @type Boolean
11534      */
11535     autoAbort : false,
11536
11537     /**
11538      * Serialize the passed form into a url encoded string
11539      * @param {String/HTMLElement} form
11540      * @return {String}
11541      */
11542     serializeForm : function(form){
11543         return Roo.lib.Ajax.serializeForm(form);
11544     }
11545 });/*
11546  * Based on:
11547  * Ext JS Library 1.1.1
11548  * Copyright(c) 2006-2007, Ext JS, LLC.
11549  *
11550  * Originally Released Under LGPL - original licence link has changed is not relivant.
11551  *
11552  * Fork - LGPL
11553  * <script type="text/javascript">
11554  */
11555  
11556 /**
11557  * @class Roo.Ajax
11558  * @extends Roo.data.Connection
11559  * Global Ajax request class.
11560  *
11561  * @instanceOf  Roo.data.Connection
11562  */
11563 Roo.Ajax = new Roo.data.Connection({
11564     // fix up the docs
11565     
11566     /**
11567      * fix up scoping
11568      * @scope Roo.Ajax
11569      */
11570     
11571    /**
11572      * @cfg {String} url @hide
11573      */
11574     /**
11575      * @cfg {Object} extraParams @hide
11576      */
11577     /**
11578      * @cfg {Object} defaultHeaders @hide
11579      */
11580     /**
11581      * @cfg {String} method (Optional) @hide
11582      */
11583     /**
11584      * @cfg {Number} timeout (Optional) @hide
11585      */
11586     /**
11587      * @cfg {Boolean} autoAbort (Optional) @hide
11588      */
11589
11590     /**
11591      * @cfg {Boolean} disableCaching (Optional) @hide
11592      */
11593
11594     /**
11595      * @property  disableCaching
11596      * True to add a unique cache-buster param to GET requests. (defaults to true)
11597      * @type Boolean
11598      */
11599     /**
11600      * @property  url
11601      * The default URL to be used for requests to the server. (defaults to undefined)
11602      * @type String
11603      */
11604     /**
11605      * @property  extraParams
11606      * An object containing properties which are used as
11607      * extra parameters to each request made by this object. (defaults to undefined)
11608      * @type Object
11609      */
11610     /**
11611      * @property  defaultHeaders
11612      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11613      * @type Object
11614      */
11615     /**
11616      * @property  method
11617      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11618      * @type String
11619      */
11620     /**
11621      * @property  timeout
11622      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11623      * @type Number
11624      */
11625
11626     /**
11627      * @property  autoAbort
11628      * Whether a new request should abort any pending requests. (defaults to false)
11629      * @type Boolean
11630      */
11631     autoAbort : false,
11632
11633     /**
11634      * Serialize the passed form into a url encoded string
11635      * @param {String/HTMLElement} form
11636      * @return {String}
11637      */
11638     serializeForm : function(form){
11639         return Roo.lib.Ajax.serializeForm(form);
11640     }
11641 });/*
11642  * Based on:
11643  * Ext JS Library 1.1.1
11644  * Copyright(c) 2006-2007, Ext JS, LLC.
11645  *
11646  * Originally Released Under LGPL - original licence link has changed is not relivant.
11647  *
11648  * Fork - LGPL
11649  * <script type="text/javascript">
11650  */
11651
11652  
11653 /**
11654  * @class Roo.UpdateManager
11655  * @extends Roo.util.Observable
11656  * Provides AJAX-style update for Element object.<br><br>
11657  * Usage:<br>
11658  * <pre><code>
11659  * // Get it from a Roo.Element object
11660  * var el = Roo.get("foo");
11661  * var mgr = el.getUpdateManager();
11662  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11663  * ...
11664  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11665  * <br>
11666  * // or directly (returns the same UpdateManager instance)
11667  * var mgr = new Roo.UpdateManager("myElementId");
11668  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11669  * mgr.on("update", myFcnNeedsToKnow);
11670  * <br>
11671    // short handed call directly from the element object
11672    Roo.get("foo").load({
11673         url: "bar.php",
11674         scripts:true,
11675         params: "for=bar",
11676         text: "Loading Foo..."
11677    });
11678  * </code></pre>
11679  * @constructor
11680  * Create new UpdateManager directly.
11681  * @param {String/HTMLElement/Roo.Element} el The element to update
11682  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11683  */
11684 Roo.UpdateManager = function(el, forceNew){
11685     el = Roo.get(el);
11686     if(!forceNew && el.updateManager){
11687         return el.updateManager;
11688     }
11689     /**
11690      * The Element object
11691      * @type Roo.Element
11692      */
11693     this.el = el;
11694     /**
11695      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11696      * @type String
11697      */
11698     this.defaultUrl = null;
11699
11700     this.addEvents({
11701         /**
11702          * @event beforeupdate
11703          * Fired before an update is made, return false from your handler and the update is cancelled.
11704          * @param {Roo.Element} el
11705          * @param {String/Object/Function} url
11706          * @param {String/Object} params
11707          */
11708         "beforeupdate": true,
11709         /**
11710          * @event update
11711          * Fired after successful update is made.
11712          * @param {Roo.Element} el
11713          * @param {Object} oResponseObject The response Object
11714          */
11715         "update": true,
11716         /**
11717          * @event failure
11718          * Fired on update failure.
11719          * @param {Roo.Element} el
11720          * @param {Object} oResponseObject The response Object
11721          */
11722         "failure": true
11723     });
11724     var d = Roo.UpdateManager.defaults;
11725     /**
11726      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11727      * @type String
11728      */
11729     this.sslBlankUrl = d.sslBlankUrl;
11730     /**
11731      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11732      * @type Boolean
11733      */
11734     this.disableCaching = d.disableCaching;
11735     /**
11736      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11737      * @type String
11738      */
11739     this.indicatorText = d.indicatorText;
11740     /**
11741      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11742      * @type String
11743      */
11744     this.showLoadIndicator = d.showLoadIndicator;
11745     /**
11746      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11747      * @type Number
11748      */
11749     this.timeout = d.timeout;
11750
11751     /**
11752      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11753      * @type Boolean
11754      */
11755     this.loadScripts = d.loadScripts;
11756
11757     /**
11758      * Transaction object of current executing transaction
11759      */
11760     this.transaction = null;
11761
11762     /**
11763      * @private
11764      */
11765     this.autoRefreshProcId = null;
11766     /**
11767      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11768      * @type Function
11769      */
11770     this.refreshDelegate = this.refresh.createDelegate(this);
11771     /**
11772      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11773      * @type Function
11774      */
11775     this.updateDelegate = this.update.createDelegate(this);
11776     /**
11777      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11778      * @type Function
11779      */
11780     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11781     /**
11782      * @private
11783      */
11784     this.successDelegate = this.processSuccess.createDelegate(this);
11785     /**
11786      * @private
11787      */
11788     this.failureDelegate = this.processFailure.createDelegate(this);
11789
11790     if(!this.renderer){
11791      /**
11792       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11793       */
11794     this.renderer = new Roo.UpdateManager.BasicRenderer();
11795     }
11796     
11797     Roo.UpdateManager.superclass.constructor.call(this);
11798 };
11799
11800 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11801     /**
11802      * Get the Element this UpdateManager is bound to
11803      * @return {Roo.Element} The element
11804      */
11805     getEl : function(){
11806         return this.el;
11807     },
11808     /**
11809      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11810      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11811 <pre><code>
11812 um.update({<br/>
11813     url: "your-url.php",<br/>
11814     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11815     callback: yourFunction,<br/>
11816     scope: yourObject, //(optional scope)  <br/>
11817     discardUrl: false, <br/>
11818     nocache: false,<br/>
11819     text: "Loading...",<br/>
11820     timeout: 30,<br/>
11821     scripts: false<br/>
11822 });
11823 </code></pre>
11824      * The only required property is url. The optional properties nocache, text and scripts
11825      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11826      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11827      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11828      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11829      */
11830     update : function(url, params, callback, discardUrl){
11831         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11832             var method = this.method, cfg;
11833             if(typeof url == "object"){ // must be config object
11834                 cfg = url;
11835                 url = cfg.url;
11836                 params = params || cfg.params;
11837                 callback = callback || cfg.callback;
11838                 discardUrl = discardUrl || cfg.discardUrl;
11839                 if(callback && cfg.scope){
11840                     callback = callback.createDelegate(cfg.scope);
11841                 }
11842                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11843                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11844                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11845                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11846                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11847             }
11848             this.showLoading();
11849             if(!discardUrl){
11850                 this.defaultUrl = url;
11851             }
11852             if(typeof url == "function"){
11853                 url = url.call(this);
11854             }
11855
11856             method = method || (params ? "POST" : "GET");
11857             if(method == "GET"){
11858                 url = this.prepareUrl(url);
11859             }
11860
11861             var o = Roo.apply(cfg ||{}, {
11862                 url : url,
11863                 params: params,
11864                 success: this.successDelegate,
11865                 failure: this.failureDelegate,
11866                 callback: undefined,
11867                 timeout: (this.timeout*1000),
11868                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11869             });
11870
11871             this.transaction = Roo.Ajax.request(o);
11872         }
11873     },
11874
11875     /**
11876      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11877      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11878      * @param {String/HTMLElement} form The form Id or form element
11879      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11880      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11881      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11882      */
11883     formUpdate : function(form, url, reset, callback){
11884         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11885             if(typeof url == "function"){
11886                 url = url.call(this);
11887             }
11888             form = Roo.getDom(form);
11889             this.transaction = Roo.Ajax.request({
11890                 form: form,
11891                 url:url,
11892                 success: this.successDelegate,
11893                 failure: this.failureDelegate,
11894                 timeout: (this.timeout*1000),
11895                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11896             });
11897             this.showLoading.defer(1, this);
11898         }
11899     },
11900
11901     /**
11902      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11903      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11904      */
11905     refresh : function(callback){
11906         if(this.defaultUrl == null){
11907             return;
11908         }
11909         this.update(this.defaultUrl, null, callback, true);
11910     },
11911
11912     /**
11913      * Set this element to auto refresh.
11914      * @param {Number} interval How often to update (in seconds).
11915      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11916      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11917      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11918      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11919      */
11920     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11921         if(refreshNow){
11922             this.update(url || this.defaultUrl, params, callback, true);
11923         }
11924         if(this.autoRefreshProcId){
11925             clearInterval(this.autoRefreshProcId);
11926         }
11927         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11928     },
11929
11930     /**
11931      * Stop auto refresh on this element.
11932      */
11933      stopAutoRefresh : function(){
11934         if(this.autoRefreshProcId){
11935             clearInterval(this.autoRefreshProcId);
11936             delete this.autoRefreshProcId;
11937         }
11938     },
11939
11940     isAutoRefreshing : function(){
11941        return this.autoRefreshProcId ? true : false;
11942     },
11943     /**
11944      * Called to update the element to "Loading" state. Override to perform custom action.
11945      */
11946     showLoading : function(){
11947         if(this.showLoadIndicator){
11948             this.el.update(this.indicatorText);
11949         }
11950     },
11951
11952     /**
11953      * Adds unique parameter to query string if disableCaching = true
11954      * @private
11955      */
11956     prepareUrl : function(url){
11957         if(this.disableCaching){
11958             var append = "_dc=" + (new Date().getTime());
11959             if(url.indexOf("?") !== -1){
11960                 url += "&" + append;
11961             }else{
11962                 url += "?" + append;
11963             }
11964         }
11965         return url;
11966     },
11967
11968     /**
11969      * @private
11970      */
11971     processSuccess : function(response){
11972         this.transaction = null;
11973         if(response.argument.form && response.argument.reset){
11974             try{ // put in try/catch since some older FF releases had problems with this
11975                 response.argument.form.reset();
11976             }catch(e){}
11977         }
11978         if(this.loadScripts){
11979             this.renderer.render(this.el, response, this,
11980                 this.updateComplete.createDelegate(this, [response]));
11981         }else{
11982             this.renderer.render(this.el, response, this);
11983             this.updateComplete(response);
11984         }
11985     },
11986
11987     updateComplete : function(response){
11988         this.fireEvent("update", this.el, response);
11989         if(typeof response.argument.callback == "function"){
11990             response.argument.callback(this.el, true, response);
11991         }
11992     },
11993
11994     /**
11995      * @private
11996      */
11997     processFailure : function(response){
11998         this.transaction = null;
11999         this.fireEvent("failure", this.el, response);
12000         if(typeof response.argument.callback == "function"){
12001             response.argument.callback(this.el, false, response);
12002         }
12003     },
12004
12005     /**
12006      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12007      * @param {Object} renderer The object implementing the render() method
12008      */
12009     setRenderer : function(renderer){
12010         this.renderer = renderer;
12011     },
12012
12013     getRenderer : function(){
12014        return this.renderer;
12015     },
12016
12017     /**
12018      * Set the defaultUrl used for updates
12019      * @param {String/Function} defaultUrl The url or a function to call to get the url
12020      */
12021     setDefaultUrl : function(defaultUrl){
12022         this.defaultUrl = defaultUrl;
12023     },
12024
12025     /**
12026      * Aborts the executing transaction
12027      */
12028     abort : function(){
12029         if(this.transaction){
12030             Roo.Ajax.abort(this.transaction);
12031         }
12032     },
12033
12034     /**
12035      * Returns true if an update is in progress
12036      * @return {Boolean}
12037      */
12038     isUpdating : function(){
12039         if(this.transaction){
12040             return Roo.Ajax.isLoading(this.transaction);
12041         }
12042         return false;
12043     }
12044 });
12045
12046 /**
12047  * @class Roo.UpdateManager.defaults
12048  * @static (not really - but it helps the doc tool)
12049  * The defaults collection enables customizing the default properties of UpdateManager
12050  */
12051    Roo.UpdateManager.defaults = {
12052        /**
12053          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12054          * @type Number
12055          */
12056          timeout : 30,
12057
12058          /**
12059          * True to process scripts by default (Defaults to false).
12060          * @type Boolean
12061          */
12062         loadScripts : false,
12063
12064         /**
12065         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12066         * @type String
12067         */
12068         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12069         /**
12070          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12071          * @type Boolean
12072          */
12073         disableCaching : false,
12074         /**
12075          * Whether to show indicatorText when loading (Defaults to true).
12076          * @type Boolean
12077          */
12078         showLoadIndicator : true,
12079         /**
12080          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12081          * @type String
12082          */
12083         indicatorText : '<div class="loading-indicator">Loading...</div>'
12084    };
12085
12086 /**
12087  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12088  *Usage:
12089  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12090  * @param {String/HTMLElement/Roo.Element} el The element to update
12091  * @param {String} url The url
12092  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12093  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12094  * @static
12095  * @deprecated
12096  * @member Roo.UpdateManager
12097  */
12098 Roo.UpdateManager.updateElement = function(el, url, params, options){
12099     var um = Roo.get(el, true).getUpdateManager();
12100     Roo.apply(um, options);
12101     um.update(url, params, options ? options.callback : null);
12102 };
12103 // alias for backwards compat
12104 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12105 /**
12106  * @class Roo.UpdateManager.BasicRenderer
12107  * Default Content renderer. Updates the elements innerHTML with the responseText.
12108  */
12109 Roo.UpdateManager.BasicRenderer = function(){};
12110
12111 Roo.UpdateManager.BasicRenderer.prototype = {
12112     /**
12113      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12114      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12115      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12116      * @param {Roo.Element} el The element being rendered
12117      * @param {Object} response The YUI Connect response object
12118      * @param {UpdateManager} updateManager The calling update manager
12119      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12120      */
12121      render : function(el, response, updateManager, callback){
12122         el.update(response.responseText, updateManager.loadScripts, callback);
12123     }
12124 };
12125 /*
12126  * Based on:
12127  * Ext JS Library 1.1.1
12128  * Copyright(c) 2006-2007, Ext JS, LLC.
12129  *
12130  * Originally Released Under LGPL - original licence link has changed is not relivant.
12131  *
12132  * Fork - LGPL
12133  * <script type="text/javascript">
12134  */
12135
12136 /**
12137  * @class Roo.util.DelayedTask
12138  * Provides a convenient method of performing setTimeout where a new
12139  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12140  * You can use this class to buffer
12141  * the keypress events for a certain number of milliseconds, and perform only if they stop
12142  * for that amount of time.
12143  * @constructor The parameters to this constructor serve as defaults and are not required.
12144  * @param {Function} fn (optional) The default function to timeout
12145  * @param {Object} scope (optional) The default scope of that timeout
12146  * @param {Array} args (optional) The default Array of arguments
12147  */
12148 Roo.util.DelayedTask = function(fn, scope, args){
12149     var id = null, d, t;
12150
12151     var call = function(){
12152         var now = new Date().getTime();
12153         if(now - t >= d){
12154             clearInterval(id);
12155             id = null;
12156             fn.apply(scope, args || []);
12157         }
12158     };
12159     /**
12160      * Cancels any pending timeout and queues a new one
12161      * @param {Number} delay The milliseconds to delay
12162      * @param {Function} newFn (optional) Overrides function passed to constructor
12163      * @param {Object} newScope (optional) Overrides scope passed to constructor
12164      * @param {Array} newArgs (optional) Overrides args passed to constructor
12165      */
12166     this.delay = function(delay, newFn, newScope, newArgs){
12167         if(id && delay != d){
12168             this.cancel();
12169         }
12170         d = delay;
12171         t = new Date().getTime();
12172         fn = newFn || fn;
12173         scope = newScope || scope;
12174         args = newArgs || args;
12175         if(!id){
12176             id = setInterval(call, d);
12177         }
12178     };
12179
12180     /**
12181      * Cancel the last queued timeout
12182      */
12183     this.cancel = function(){
12184         if(id){
12185             clearInterval(id);
12186             id = null;
12187         }
12188     };
12189 };/*
12190  * Based on:
12191  * Ext JS Library 1.1.1
12192  * Copyright(c) 2006-2007, Ext JS, LLC.
12193  *
12194  * Originally Released Under LGPL - original licence link has changed is not relivant.
12195  *
12196  * Fork - LGPL
12197  * <script type="text/javascript">
12198  */
12199  
12200  
12201 Roo.util.TaskRunner = function(interval){
12202     interval = interval || 10;
12203     var tasks = [], removeQueue = [];
12204     var id = 0;
12205     var running = false;
12206
12207     var stopThread = function(){
12208         running = false;
12209         clearInterval(id);
12210         id = 0;
12211     };
12212
12213     var startThread = function(){
12214         if(!running){
12215             running = true;
12216             id = setInterval(runTasks, interval);
12217         }
12218     };
12219
12220     var removeTask = function(task){
12221         removeQueue.push(task);
12222         if(task.onStop){
12223             task.onStop();
12224         }
12225     };
12226
12227     var runTasks = function(){
12228         if(removeQueue.length > 0){
12229             for(var i = 0, len = removeQueue.length; i < len; i++){
12230                 tasks.remove(removeQueue[i]);
12231             }
12232             removeQueue = [];
12233             if(tasks.length < 1){
12234                 stopThread();
12235                 return;
12236             }
12237         }
12238         var now = new Date().getTime();
12239         for(var i = 0, len = tasks.length; i < len; ++i){
12240             var t = tasks[i];
12241             var itime = now - t.taskRunTime;
12242             if(t.interval <= itime){
12243                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12244                 t.taskRunTime = now;
12245                 if(rt === false || t.taskRunCount === t.repeat){
12246                     removeTask(t);
12247                     return;
12248                 }
12249             }
12250             if(t.duration && t.duration <= (now - t.taskStartTime)){
12251                 removeTask(t);
12252             }
12253         }
12254     };
12255
12256     /**
12257      * Queues a new task.
12258      * @param {Object} task
12259      */
12260     this.start = function(task){
12261         tasks.push(task);
12262         task.taskStartTime = new Date().getTime();
12263         task.taskRunTime = 0;
12264         task.taskRunCount = 0;
12265         startThread();
12266         return task;
12267     };
12268
12269     this.stop = function(task){
12270         removeTask(task);
12271         return task;
12272     };
12273
12274     this.stopAll = function(){
12275         stopThread();
12276         for(var i = 0, len = tasks.length; i < len; i++){
12277             if(tasks[i].onStop){
12278                 tasks[i].onStop();
12279             }
12280         }
12281         tasks = [];
12282         removeQueue = [];
12283     };
12284 };
12285
12286 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12287  * Based on:
12288  * Ext JS Library 1.1.1
12289  * Copyright(c) 2006-2007, Ext JS, LLC.
12290  *
12291  * Originally Released Under LGPL - original licence link has changed is not relivant.
12292  *
12293  * Fork - LGPL
12294  * <script type="text/javascript">
12295  */
12296
12297  
12298 /**
12299  * @class Roo.util.MixedCollection
12300  * @extends Roo.util.Observable
12301  * A Collection class that maintains both numeric indexes and keys and exposes events.
12302  * @constructor
12303  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12304  * collection (defaults to false)
12305  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12306  * and return the key value for that item.  This is used when available to look up the key on items that
12307  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12308  * equivalent to providing an implementation for the {@link #getKey} method.
12309  */
12310 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12311     this.items = [];
12312     this.map = {};
12313     this.keys = [];
12314     this.length = 0;
12315     this.addEvents({
12316         /**
12317          * @event clear
12318          * Fires when the collection is cleared.
12319          */
12320         "clear" : true,
12321         /**
12322          * @event add
12323          * Fires when an item is added to the collection.
12324          * @param {Number} index The index at which the item was added.
12325          * @param {Object} o The item added.
12326          * @param {String} key The key associated with the added item.
12327          */
12328         "add" : true,
12329         /**
12330          * @event replace
12331          * Fires when an item is replaced in the collection.
12332          * @param {String} key he key associated with the new added.
12333          * @param {Object} old The item being replaced.
12334          * @param {Object} new The new item.
12335          */
12336         "replace" : true,
12337         /**
12338          * @event remove
12339          * Fires when an item is removed from the collection.
12340          * @param {Object} o The item being removed.
12341          * @param {String} key (optional) The key associated with the removed item.
12342          */
12343         "remove" : true,
12344         "sort" : true
12345     });
12346     this.allowFunctions = allowFunctions === true;
12347     if(keyFn){
12348         this.getKey = keyFn;
12349     }
12350     Roo.util.MixedCollection.superclass.constructor.call(this);
12351 };
12352
12353 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12354     allowFunctions : false,
12355     
12356 /**
12357  * Adds an item to the collection.
12358  * @param {String} key The key to associate with the item
12359  * @param {Object} o The item to add.
12360  * @return {Object} The item added.
12361  */
12362     add : function(key, o){
12363         if(arguments.length == 1){
12364             o = arguments[0];
12365             key = this.getKey(o);
12366         }
12367         if(typeof key == "undefined" || key === null){
12368             this.length++;
12369             this.items.push(o);
12370             this.keys.push(null);
12371         }else{
12372             var old = this.map[key];
12373             if(old){
12374                 return this.replace(key, o);
12375             }
12376             this.length++;
12377             this.items.push(o);
12378             this.map[key] = o;
12379             this.keys.push(key);
12380         }
12381         this.fireEvent("add", this.length-1, o, key);
12382         return o;
12383     },
12384        
12385 /**
12386   * MixedCollection has a generic way to fetch keys if you implement getKey.
12387 <pre><code>
12388 // normal way
12389 var mc = new Roo.util.MixedCollection();
12390 mc.add(someEl.dom.id, someEl);
12391 mc.add(otherEl.dom.id, otherEl);
12392 //and so on
12393
12394 // using getKey
12395 var mc = new Roo.util.MixedCollection();
12396 mc.getKey = function(el){
12397    return el.dom.id;
12398 };
12399 mc.add(someEl);
12400 mc.add(otherEl);
12401
12402 // or via the constructor
12403 var mc = new Roo.util.MixedCollection(false, function(el){
12404    return el.dom.id;
12405 });
12406 mc.add(someEl);
12407 mc.add(otherEl);
12408 </code></pre>
12409  * @param o {Object} The item for which to find the key.
12410  * @return {Object} The key for the passed item.
12411  */
12412     getKey : function(o){
12413          return o.id; 
12414     },
12415    
12416 /**
12417  * Replaces an item in the collection.
12418  * @param {String} key The key associated with the item to replace, or the item to replace.
12419  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12420  * @return {Object}  The new item.
12421  */
12422     replace : function(key, o){
12423         if(arguments.length == 1){
12424             o = arguments[0];
12425             key = this.getKey(o);
12426         }
12427         var old = this.item(key);
12428         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12429              return this.add(key, o);
12430         }
12431         var index = this.indexOfKey(key);
12432         this.items[index] = o;
12433         this.map[key] = o;
12434         this.fireEvent("replace", key, old, o);
12435         return o;
12436     },
12437    
12438 /**
12439  * Adds all elements of an Array or an Object to the collection.
12440  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12441  * an Array of values, each of which are added to the collection.
12442  */
12443     addAll : function(objs){
12444         if(arguments.length > 1 || objs instanceof Array){
12445             var args = arguments.length > 1 ? arguments : objs;
12446             for(var i = 0, len = args.length; i < len; i++){
12447                 this.add(args[i]);
12448             }
12449         }else{
12450             for(var key in objs){
12451                 if(this.allowFunctions || typeof objs[key] != "function"){
12452                     this.add(key, objs[key]);
12453                 }
12454             }
12455         }
12456     },
12457    
12458 /**
12459  * Executes the specified function once for every item in the collection, passing each
12460  * item as the first and only parameter. returning false from the function will stop the iteration.
12461  * @param {Function} fn The function to execute for each item.
12462  * @param {Object} scope (optional) The scope in which to execute the function.
12463  */
12464     each : function(fn, scope){
12465         var items = [].concat(this.items); // each safe for removal
12466         for(var i = 0, len = items.length; i < len; i++){
12467             if(fn.call(scope || items[i], items[i], i, len) === false){
12468                 break;
12469             }
12470         }
12471     },
12472    
12473 /**
12474  * Executes the specified function once for every key in the collection, passing each
12475  * key, and its associated item as the first two parameters.
12476  * @param {Function} fn The function to execute for each item.
12477  * @param {Object} scope (optional) The scope in which to execute the function.
12478  */
12479     eachKey : function(fn, scope){
12480         for(var i = 0, len = this.keys.length; i < len; i++){
12481             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12482         }
12483     },
12484    
12485 /**
12486  * Returns the first item in the collection which elicits a true return value from the
12487  * passed selection function.
12488  * @param {Function} fn The selection function to execute for each item.
12489  * @param {Object} scope (optional) The scope in which to execute the function.
12490  * @return {Object} The first item in the collection which returned true from the selection function.
12491  */
12492     find : function(fn, scope){
12493         for(var i = 0, len = this.items.length; i < len; i++){
12494             if(fn.call(scope || window, this.items[i], this.keys[i])){
12495                 return this.items[i];
12496             }
12497         }
12498         return null;
12499     },
12500    
12501 /**
12502  * Inserts an item at the specified index in the collection.
12503  * @param {Number} index The index to insert the item at.
12504  * @param {String} key The key to associate with the new item, or the item itself.
12505  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12506  * @return {Object} The item inserted.
12507  */
12508     insert : function(index, key, o){
12509         if(arguments.length == 2){
12510             o = arguments[1];
12511             key = this.getKey(o);
12512         }
12513         if(index >= this.length){
12514             return this.add(key, o);
12515         }
12516         this.length++;
12517         this.items.splice(index, 0, o);
12518         if(typeof key != "undefined" && key != null){
12519             this.map[key] = o;
12520         }
12521         this.keys.splice(index, 0, key);
12522         this.fireEvent("add", index, o, key);
12523         return o;
12524     },
12525    
12526 /**
12527  * Removed an item from the collection.
12528  * @param {Object} o The item to remove.
12529  * @return {Object} The item removed.
12530  */
12531     remove : function(o){
12532         return this.removeAt(this.indexOf(o));
12533     },
12534    
12535 /**
12536  * Remove an item from a specified index in the collection.
12537  * @param {Number} index The index within the collection of the item to remove.
12538  */
12539     removeAt : function(index){
12540         if(index < this.length && index >= 0){
12541             this.length--;
12542             var o = this.items[index];
12543             this.items.splice(index, 1);
12544             var key = this.keys[index];
12545             if(typeof key != "undefined"){
12546                 delete this.map[key];
12547             }
12548             this.keys.splice(index, 1);
12549             this.fireEvent("remove", o, key);
12550         }
12551     },
12552    
12553 /**
12554  * Removed an item associated with the passed key fom the collection.
12555  * @param {String} key The key of the item to remove.
12556  */
12557     removeKey : function(key){
12558         return this.removeAt(this.indexOfKey(key));
12559     },
12560    
12561 /**
12562  * Returns the number of items in the collection.
12563  * @return {Number} the number of items in the collection.
12564  */
12565     getCount : function(){
12566         return this.length; 
12567     },
12568    
12569 /**
12570  * Returns index within the collection of the passed Object.
12571  * @param {Object} o The item to find the index of.
12572  * @return {Number} index of the item.
12573  */
12574     indexOf : function(o){
12575         if(!this.items.indexOf){
12576             for(var i = 0, len = this.items.length; i < len; i++){
12577                 if(this.items[i] == o) return i;
12578             }
12579             return -1;
12580         }else{
12581             return this.items.indexOf(o);
12582         }
12583     },
12584    
12585 /**
12586  * Returns index within the collection of the passed key.
12587  * @param {String} key The key to find the index of.
12588  * @return {Number} index of the key.
12589  */
12590     indexOfKey : function(key){
12591         if(!this.keys.indexOf){
12592             for(var i = 0, len = this.keys.length; i < len; i++){
12593                 if(this.keys[i] == key) return i;
12594             }
12595             return -1;
12596         }else{
12597             return this.keys.indexOf(key);
12598         }
12599     },
12600    
12601 /**
12602  * Returns the item associated with the passed key OR index. Key has priority over index.
12603  * @param {String/Number} key The key or index of the item.
12604  * @return {Object} The item associated with the passed key.
12605  */
12606     item : function(key){
12607         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12608         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12609     },
12610     
12611 /**
12612  * Returns the item at the specified index.
12613  * @param {Number} index The index of the item.
12614  * @return {Object}
12615  */
12616     itemAt : function(index){
12617         return this.items[index];
12618     },
12619     
12620 /**
12621  * Returns the item associated with the passed key.
12622  * @param {String/Number} key The key of the item.
12623  * @return {Object} The item associated with the passed key.
12624  */
12625     key : function(key){
12626         return this.map[key];
12627     },
12628    
12629 /**
12630  * Returns true if the collection contains the passed Object as an item.
12631  * @param {Object} o  The Object to look for in the collection.
12632  * @return {Boolean} True if the collection contains the Object as an item.
12633  */
12634     contains : function(o){
12635         return this.indexOf(o) != -1;
12636     },
12637    
12638 /**
12639  * Returns true if the collection contains the passed Object as a key.
12640  * @param {String} key The key to look for in the collection.
12641  * @return {Boolean} True if the collection contains the Object as a key.
12642  */
12643     containsKey : function(key){
12644         return typeof this.map[key] != "undefined";
12645     },
12646    
12647 /**
12648  * Removes all items from the collection.
12649  */
12650     clear : function(){
12651         this.length = 0;
12652         this.items = [];
12653         this.keys = [];
12654         this.map = {};
12655         this.fireEvent("clear");
12656     },
12657    
12658 /**
12659  * Returns the first item in the collection.
12660  * @return {Object} the first item in the collection..
12661  */
12662     first : function(){
12663         return this.items[0]; 
12664     },
12665    
12666 /**
12667  * Returns the last item in the collection.
12668  * @return {Object} the last item in the collection..
12669  */
12670     last : function(){
12671         return this.items[this.length-1];   
12672     },
12673     
12674     _sort : function(property, dir, fn){
12675         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12676         fn = fn || function(a, b){
12677             return a-b;
12678         };
12679         var c = [], k = this.keys, items = this.items;
12680         for(var i = 0, len = items.length; i < len; i++){
12681             c[c.length] = {key: k[i], value: items[i], index: i};
12682         }
12683         c.sort(function(a, b){
12684             var v = fn(a[property], b[property]) * dsc;
12685             if(v == 0){
12686                 v = (a.index < b.index ? -1 : 1);
12687             }
12688             return v;
12689         });
12690         for(var i = 0, len = c.length; i < len; i++){
12691             items[i] = c[i].value;
12692             k[i] = c[i].key;
12693         }
12694         this.fireEvent("sort", this);
12695     },
12696     
12697     /**
12698      * Sorts this collection with the passed comparison function
12699      * @param {String} direction (optional) "ASC" or "DESC"
12700      * @param {Function} fn (optional) comparison function
12701      */
12702     sort : function(dir, fn){
12703         this._sort("value", dir, fn);
12704     },
12705     
12706     /**
12707      * Sorts this collection by keys
12708      * @param {String} direction (optional) "ASC" or "DESC"
12709      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12710      */
12711     keySort : function(dir, fn){
12712         this._sort("key", dir, fn || function(a, b){
12713             return String(a).toUpperCase()-String(b).toUpperCase();
12714         });
12715     },
12716     
12717     /**
12718      * Returns a range of items in this collection
12719      * @param {Number} startIndex (optional) defaults to 0
12720      * @param {Number} endIndex (optional) default to the last item
12721      * @return {Array} An array of items
12722      */
12723     getRange : function(start, end){
12724         var items = this.items;
12725         if(items.length < 1){
12726             return [];
12727         }
12728         start = start || 0;
12729         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12730         var r = [];
12731         if(start <= end){
12732             for(var i = start; i <= end; i++) {
12733                     r[r.length] = items[i];
12734             }
12735         }else{
12736             for(var i = start; i >= end; i--) {
12737                     r[r.length] = items[i];
12738             }
12739         }
12740         return r;
12741     },
12742         
12743     /**
12744      * Filter the <i>objects</i> in this collection by a specific property. 
12745      * Returns a new collection that has been filtered.
12746      * @param {String} property A property on your objects
12747      * @param {String/RegExp} value Either string that the property values 
12748      * should start with or a RegExp to test against the property
12749      * @return {MixedCollection} The new filtered collection
12750      */
12751     filter : function(property, value){
12752         if(!value.exec){ // not a regex
12753             value = String(value);
12754             if(value.length == 0){
12755                 return this.clone();
12756             }
12757             value = new RegExp("^" + Roo.escapeRe(value), "i");
12758         }
12759         return this.filterBy(function(o){
12760             return o && value.test(o[property]);
12761         });
12762         },
12763     
12764     /**
12765      * Filter by a function. * Returns a new collection that has been filtered.
12766      * The passed function will be called with each 
12767      * object in the collection. If the function returns true, the value is included 
12768      * otherwise it is filtered.
12769      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12770      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12771      * @return {MixedCollection} The new filtered collection
12772      */
12773     filterBy : function(fn, scope){
12774         var r = new Roo.util.MixedCollection();
12775         r.getKey = this.getKey;
12776         var k = this.keys, it = this.items;
12777         for(var i = 0, len = it.length; i < len; i++){
12778             if(fn.call(scope||this, it[i], k[i])){
12779                                 r.add(k[i], it[i]);
12780                         }
12781         }
12782         return r;
12783     },
12784     
12785     /**
12786      * Creates a duplicate of this collection
12787      * @return {MixedCollection}
12788      */
12789     clone : function(){
12790         var r = new Roo.util.MixedCollection();
12791         var k = this.keys, it = this.items;
12792         for(var i = 0, len = it.length; i < len; i++){
12793             r.add(k[i], it[i]);
12794         }
12795         r.getKey = this.getKey;
12796         return r;
12797     }
12798 });
12799 /**
12800  * Returns the item associated with the passed key or index.
12801  * @method
12802  * @param {String/Number} key The key or index of the item.
12803  * @return {Object} The item associated with the passed key.
12804  */
12805 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12806  * Based on:
12807  * Ext JS Library 1.1.1
12808  * Copyright(c) 2006-2007, Ext JS, LLC.
12809  *
12810  * Originally Released Under LGPL - original licence link has changed is not relivant.
12811  *
12812  * Fork - LGPL
12813  * <script type="text/javascript">
12814  */
12815 /**
12816  * @class Roo.util.JSON
12817  * Modified version of Douglas Crockford"s json.js that doesn"t
12818  * mess with the Object prototype 
12819  * http://www.json.org/js.html
12820  * @singleton
12821  */
12822 Roo.util.JSON = new (function(){
12823     var useHasOwn = {}.hasOwnProperty ? true : false;
12824     
12825     // crashes Safari in some instances
12826     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12827     
12828     var pad = function(n) {
12829         return n < 10 ? "0" + n : n;
12830     };
12831     
12832     var m = {
12833         "\b": '\\b',
12834         "\t": '\\t',
12835         "\n": '\\n',
12836         "\f": '\\f',
12837         "\r": '\\r',
12838         '"' : '\\"',
12839         "\\": '\\\\'
12840     };
12841
12842     var encodeString = function(s){
12843         if (/["\\\x00-\x1f]/.test(s)) {
12844             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12845                 var c = m[b];
12846                 if(c){
12847                     return c;
12848                 }
12849                 c = b.charCodeAt();
12850                 return "\\u00" +
12851                     Math.floor(c / 16).toString(16) +
12852                     (c % 16).toString(16);
12853             }) + '"';
12854         }
12855         return '"' + s + '"';
12856     };
12857     
12858     var encodeArray = function(o){
12859         var a = ["["], b, i, l = o.length, v;
12860             for (i = 0; i < l; i += 1) {
12861                 v = o[i];
12862                 switch (typeof v) {
12863                     case "undefined":
12864                     case "function":
12865                     case "unknown":
12866                         break;
12867                     default:
12868                         if (b) {
12869                             a.push(',');
12870                         }
12871                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12872                         b = true;
12873                 }
12874             }
12875             a.push("]");
12876             return a.join("");
12877     };
12878     
12879     var encodeDate = function(o){
12880         return '"' + o.getFullYear() + "-" +
12881                 pad(o.getMonth() + 1) + "-" +
12882                 pad(o.getDate()) + "T" +
12883                 pad(o.getHours()) + ":" +
12884                 pad(o.getMinutes()) + ":" +
12885                 pad(o.getSeconds()) + '"';
12886     };
12887     
12888     /**
12889      * Encodes an Object, Array or other value
12890      * @param {Mixed} o The variable to encode
12891      * @return {String} The JSON string
12892      */
12893     this.encode = function(o)
12894     {
12895         // should this be extended to fully wrap stringify..
12896         
12897         if(typeof o == "undefined" || o === null){
12898             return "null";
12899         }else if(o instanceof Array){
12900             return encodeArray(o);
12901         }else if(o instanceof Date){
12902             return encodeDate(o);
12903         }else if(typeof o == "string"){
12904             return encodeString(o);
12905         }else if(typeof o == "number"){
12906             return isFinite(o) ? String(o) : "null";
12907         }else if(typeof o == "boolean"){
12908             return String(o);
12909         }else {
12910             var a = ["{"], b, i, v;
12911             for (i in o) {
12912                 if(!useHasOwn || o.hasOwnProperty(i)) {
12913                     v = o[i];
12914                     switch (typeof v) {
12915                     case "undefined":
12916                     case "function":
12917                     case "unknown":
12918                         break;
12919                     default:
12920                         if(b){
12921                             a.push(',');
12922                         }
12923                         a.push(this.encode(i), ":",
12924                                 v === null ? "null" : this.encode(v));
12925                         b = true;
12926                     }
12927                 }
12928             }
12929             a.push("}");
12930             return a.join("");
12931         }
12932     };
12933     
12934     /**
12935      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12936      * @param {String} json The JSON string
12937      * @return {Object} The resulting object
12938      */
12939     this.decode = function(json){
12940         
12941         return  /** eval:var:json */ eval("(" + json + ')');
12942     };
12943 })();
12944 /** 
12945  * Shorthand for {@link Roo.util.JSON#encode}
12946  * @member Roo encode 
12947  * @method */
12948 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12949 /** 
12950  * Shorthand for {@link Roo.util.JSON#decode}
12951  * @member Roo decode 
12952  * @method */
12953 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12954 /*
12955  * Based on:
12956  * Ext JS Library 1.1.1
12957  * Copyright(c) 2006-2007, Ext JS, LLC.
12958  *
12959  * Originally Released Under LGPL - original licence link has changed is not relivant.
12960  *
12961  * Fork - LGPL
12962  * <script type="text/javascript">
12963  */
12964  
12965 /**
12966  * @class Roo.util.Format
12967  * Reusable data formatting functions
12968  * @singleton
12969  */
12970 Roo.util.Format = function(){
12971     var trimRe = /^\s+|\s+$/g;
12972     return {
12973         /**
12974          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12975          * @param {String} value The string to truncate
12976          * @param {Number} length The maximum length to allow before truncating
12977          * @return {String} The converted text
12978          */
12979         ellipsis : function(value, len){
12980             if(value && value.length > len){
12981                 return value.substr(0, len-3)+"...";
12982             }
12983             return value;
12984         },
12985
12986         /**
12987          * Checks a reference and converts it to empty string if it is undefined
12988          * @param {Mixed} value Reference to check
12989          * @return {Mixed} Empty string if converted, otherwise the original value
12990          */
12991         undef : function(value){
12992             return typeof value != "undefined" ? value : "";
12993         },
12994
12995         /**
12996          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12997          * @param {String} value The string to encode
12998          * @return {String} The encoded text
12999          */
13000         htmlEncode : function(value){
13001             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13002         },
13003
13004         /**
13005          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13006          * @param {String} value The string to decode
13007          * @return {String} The decoded text
13008          */
13009         htmlDecode : function(value){
13010             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13011         },
13012
13013         /**
13014          * Trims any whitespace from either side of a string
13015          * @param {String} value The text to trim
13016          * @return {String} The trimmed text
13017          */
13018         trim : function(value){
13019             return String(value).replace(trimRe, "");
13020         },
13021
13022         /**
13023          * Returns a substring from within an original string
13024          * @param {String} value The original text
13025          * @param {Number} start The start index of the substring
13026          * @param {Number} length The length of the substring
13027          * @return {String} The substring
13028          */
13029         substr : function(value, start, length){
13030             return String(value).substr(start, length);
13031         },
13032
13033         /**
13034          * Converts a string to all lower case letters
13035          * @param {String} value The text to convert
13036          * @return {String} The converted text
13037          */
13038         lowercase : function(value){
13039             return String(value).toLowerCase();
13040         },
13041
13042         /**
13043          * Converts a string to all upper case letters
13044          * @param {String} value The text to convert
13045          * @return {String} The converted text
13046          */
13047         uppercase : function(value){
13048             return String(value).toUpperCase();
13049         },
13050
13051         /**
13052          * Converts the first character only of a string to upper case
13053          * @param {String} value The text to convert
13054          * @return {String} The converted text
13055          */
13056         capitalize : function(value){
13057             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13058         },
13059
13060         // private
13061         call : function(value, fn){
13062             if(arguments.length > 2){
13063                 var args = Array.prototype.slice.call(arguments, 2);
13064                 args.unshift(value);
13065                  
13066                 return /** eval:var:value */  eval(fn).apply(window, args);
13067             }else{
13068                 /** eval:var:value */
13069                 return /** eval:var:value */ eval(fn).call(window, value);
13070             }
13071         },
13072
13073        
13074         /**
13075          * safer version of Math.toFixed..??/
13076          * @param {Number/String} value The numeric value to format
13077          * @param {Number/String} value Decimal places 
13078          * @return {String} The formatted currency string
13079          */
13080         toFixed : function(v, n)
13081         {
13082             // why not use to fixed - precision is buggered???
13083             if (!n) {
13084                 return Math.round(v-0);
13085             }
13086             var fact = Math.pow(10,n+1);
13087             v = (Math.round((v-0)*fact))/fact;
13088             var z = (''+fact).substring(2);
13089             if (v == Math.floor(v)) {
13090                 return Math.floor(v) + '.' + z;
13091             }
13092             
13093             // now just padd decimals..
13094             var ps = String(v).split('.');
13095             var fd = (ps[1] + z);
13096             var r = fd.substring(0,n); 
13097             var rm = fd.substring(n); 
13098             if (rm < 5) {
13099                 return ps[0] + '.' + r;
13100             }
13101             r*=1; // turn it into a number;
13102             r++;
13103             if (String(r).length != n) {
13104                 ps[0]*=1;
13105                 ps[0]++;
13106                 r = String(r).substring(1); // chop the end off.
13107             }
13108             
13109             return ps[0] + '.' + r;
13110              
13111         },
13112         
13113         /**
13114          * Format a number as US currency
13115          * @param {Number/String} value The numeric value to format
13116          * @return {String} The formatted currency string
13117          */
13118         usMoney : function(v){
13119             v = (Math.round((v-0)*100))/100;
13120             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13121             v = String(v);
13122             var ps = v.split('.');
13123             var whole = ps[0];
13124             var sub = ps[1] ? '.'+ ps[1] : '.00';
13125             var r = /(\d+)(\d{3})/;
13126             while (r.test(whole)) {
13127                 whole = whole.replace(r, '$1' + ',' + '$2');
13128             }
13129             return "$" + whole + sub ;
13130         },
13131         
13132         /**
13133          * Parse a value into a formatted date using the specified format pattern.
13134          * @param {Mixed} value The value to format
13135          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13136          * @return {String} The formatted date string
13137          */
13138         date : function(v, format){
13139             if(!v){
13140                 return "";
13141             }
13142             if(!(v instanceof Date)){
13143                 v = new Date(Date.parse(v));
13144             }
13145             return v.dateFormat(format || "m/d/Y");
13146         },
13147
13148         /**
13149          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13150          * @param {String} format Any valid date format string
13151          * @return {Function} The date formatting function
13152          */
13153         dateRenderer : function(format){
13154             return function(v){
13155                 return Roo.util.Format.date(v, format);  
13156             };
13157         },
13158
13159         // private
13160         stripTagsRE : /<\/?[^>]+>/gi,
13161         
13162         /**
13163          * Strips all HTML tags
13164          * @param {Mixed} value The text from which to strip tags
13165          * @return {String} The stripped text
13166          */
13167         stripTags : function(v){
13168             return !v ? v : String(v).replace(this.stripTagsRE, "");
13169         }
13170     };
13171 }();/*
13172  * Based on:
13173  * Ext JS Library 1.1.1
13174  * Copyright(c) 2006-2007, Ext JS, LLC.
13175  *
13176  * Originally Released Under LGPL - original licence link has changed is not relivant.
13177  *
13178  * Fork - LGPL
13179  * <script type="text/javascript">
13180  */
13181
13182
13183  
13184
13185 /**
13186  * @class Roo.MasterTemplate
13187  * @extends Roo.Template
13188  * Provides a template that can have child templates. The syntax is:
13189 <pre><code>
13190 var t = new Roo.MasterTemplate(
13191         '&lt;select name="{name}"&gt;',
13192                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13193         '&lt;/select&gt;'
13194 );
13195 t.add('options', {value: 'foo', text: 'bar'});
13196 // or you can add multiple child elements in one shot
13197 t.addAll('options', [
13198     {value: 'foo', text: 'bar'},
13199     {value: 'foo2', text: 'bar2'},
13200     {value: 'foo3', text: 'bar3'}
13201 ]);
13202 // then append, applying the master template values
13203 t.append('my-form', {name: 'my-select'});
13204 </code></pre>
13205 * A name attribute for the child template is not required if you have only one child
13206 * template or you want to refer to them by index.
13207  */
13208 Roo.MasterTemplate = function(){
13209     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13210     this.originalHtml = this.html;
13211     var st = {};
13212     var m, re = this.subTemplateRe;
13213     re.lastIndex = 0;
13214     var subIndex = 0;
13215     while(m = re.exec(this.html)){
13216         var name = m[1], content = m[2];
13217         st[subIndex] = {
13218             name: name,
13219             index: subIndex,
13220             buffer: [],
13221             tpl : new Roo.Template(content)
13222         };
13223         if(name){
13224             st[name] = st[subIndex];
13225         }
13226         st[subIndex].tpl.compile();
13227         st[subIndex].tpl.call = this.call.createDelegate(this);
13228         subIndex++;
13229     }
13230     this.subCount = subIndex;
13231     this.subs = st;
13232 };
13233 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13234     /**
13235     * The regular expression used to match sub templates
13236     * @type RegExp
13237     * @property
13238     */
13239     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13240
13241     /**
13242      * Applies the passed values to a child template.
13243      * @param {String/Number} name (optional) The name or index of the child template
13244      * @param {Array/Object} values The values to be applied to the template
13245      * @return {MasterTemplate} this
13246      */
13247      add : function(name, values){
13248         if(arguments.length == 1){
13249             values = arguments[0];
13250             name = 0;
13251         }
13252         var s = this.subs[name];
13253         s.buffer[s.buffer.length] = s.tpl.apply(values);
13254         return this;
13255     },
13256
13257     /**
13258      * Applies all the passed values to a child template.
13259      * @param {String/Number} name (optional) The name or index of the child template
13260      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13261      * @param {Boolean} reset (optional) True to reset the template first
13262      * @return {MasterTemplate} this
13263      */
13264     fill : function(name, values, reset){
13265         var a = arguments;
13266         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13267             values = a[0];
13268             name = 0;
13269             reset = a[1];
13270         }
13271         if(reset){
13272             this.reset();
13273         }
13274         for(var i = 0, len = values.length; i < len; i++){
13275             this.add(name, values[i]);
13276         }
13277         return this;
13278     },
13279
13280     /**
13281      * Resets the template for reuse
13282      * @return {MasterTemplate} this
13283      */
13284      reset : function(){
13285         var s = this.subs;
13286         for(var i = 0; i < this.subCount; i++){
13287             s[i].buffer = [];
13288         }
13289         return this;
13290     },
13291
13292     applyTemplate : function(values){
13293         var s = this.subs;
13294         var replaceIndex = -1;
13295         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13296             return s[++replaceIndex].buffer.join("");
13297         });
13298         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13299     },
13300
13301     apply : function(){
13302         return this.applyTemplate.apply(this, arguments);
13303     },
13304
13305     compile : function(){return this;}
13306 });
13307
13308 /**
13309  * Alias for fill().
13310  * @method
13311  */
13312 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13313  /**
13314  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13315  * var tpl = Roo.MasterTemplate.from('element-id');
13316  * @param {String/HTMLElement} el
13317  * @param {Object} config
13318  * @static
13319  */
13320 Roo.MasterTemplate.from = function(el, config){
13321     el = Roo.getDom(el);
13322     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13323 };/*
13324  * Based on:
13325  * Ext JS Library 1.1.1
13326  * Copyright(c) 2006-2007, Ext JS, LLC.
13327  *
13328  * Originally Released Under LGPL - original licence link has changed is not relivant.
13329  *
13330  * Fork - LGPL
13331  * <script type="text/javascript">
13332  */
13333
13334  
13335 /**
13336  * @class Roo.util.CSS
13337  * Utility class for manipulating CSS rules
13338  * @singleton
13339  */
13340 Roo.util.CSS = function(){
13341         var rules = null;
13342         var doc = document;
13343
13344     var camelRe = /(-[a-z])/gi;
13345     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13346
13347    return {
13348    /**
13349     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13350     * tag and appended to the HEAD of the document.
13351     * @param {String|Object} cssText The text containing the css rules
13352     * @param {String} id An id to add to the stylesheet for later removal
13353     * @return {StyleSheet}
13354     */
13355     createStyleSheet : function(cssText, id){
13356         var ss;
13357         var head = doc.getElementsByTagName("head")[0];
13358         var nrules = doc.createElement("style");
13359         nrules.setAttribute("type", "text/css");
13360         if(id){
13361             nrules.setAttribute("id", id);
13362         }
13363         if (typeof(cssText) != 'string') {
13364             // support object maps..
13365             // not sure if this a good idea.. 
13366             // perhaps it should be merged with the general css handling
13367             // and handle js style props.
13368             var cssTextNew = [];
13369             for(var n in cssText) {
13370                 var citems = [];
13371                 for(var k in cssText[n]) {
13372                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13373                 }
13374                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13375                 
13376             }
13377             cssText = cssTextNew.join("\n");
13378             
13379         }
13380        
13381        
13382        if(Roo.isIE){
13383            head.appendChild(nrules);
13384            ss = nrules.styleSheet;
13385            ss.cssText = cssText;
13386        }else{
13387            try{
13388                 nrules.appendChild(doc.createTextNode(cssText));
13389            }catch(e){
13390                nrules.cssText = cssText; 
13391            }
13392            head.appendChild(nrules);
13393            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13394        }
13395        this.cacheStyleSheet(ss);
13396        return ss;
13397    },
13398
13399    /**
13400     * Removes a style or link tag by id
13401     * @param {String} id The id of the tag
13402     */
13403    removeStyleSheet : function(id){
13404        var existing = doc.getElementById(id);
13405        if(existing){
13406            existing.parentNode.removeChild(existing);
13407        }
13408    },
13409
13410    /**
13411     * Dynamically swaps an existing stylesheet reference for a new one
13412     * @param {String} id The id of an existing link tag to remove
13413     * @param {String} url The href of the new stylesheet to include
13414     */
13415    swapStyleSheet : function(id, url){
13416        this.removeStyleSheet(id);
13417        var ss = doc.createElement("link");
13418        ss.setAttribute("rel", "stylesheet");
13419        ss.setAttribute("type", "text/css");
13420        ss.setAttribute("id", id);
13421        ss.setAttribute("href", url);
13422        doc.getElementsByTagName("head")[0].appendChild(ss);
13423    },
13424    
13425    /**
13426     * Refresh the rule cache if you have dynamically added stylesheets
13427     * @return {Object} An object (hash) of rules indexed by selector
13428     */
13429    refreshCache : function(){
13430        return this.getRules(true);
13431    },
13432
13433    // private
13434    cacheStyleSheet : function(stylesheet){
13435        if(!rules){
13436            rules = {};
13437        }
13438        try{// try catch for cross domain access issue
13439            var ssRules = stylesheet.cssRules || stylesheet.rules;
13440            for(var j = ssRules.length-1; j >= 0; --j){
13441                rules[ssRules[j].selectorText] = ssRules[j];
13442            }
13443        }catch(e){}
13444    },
13445    
13446    /**
13447     * Gets all css rules for the document
13448     * @param {Boolean} refreshCache true to refresh the internal cache
13449     * @return {Object} An object (hash) of rules indexed by selector
13450     */
13451    getRules : function(refreshCache){
13452                 if(rules == null || refreshCache){
13453                         rules = {};
13454                         var ds = doc.styleSheets;
13455                         for(var i =0, len = ds.length; i < len; i++){
13456                             try{
13457                         this.cacheStyleSheet(ds[i]);
13458                     }catch(e){} 
13459                 }
13460                 }
13461                 return rules;
13462         },
13463         
13464         /**
13465     * Gets an an individual CSS rule by selector(s)
13466     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13467     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13468     * @return {CSSRule} The CSS rule or null if one is not found
13469     */
13470    getRule : function(selector, refreshCache){
13471                 var rs = this.getRules(refreshCache);
13472                 if(!(selector instanceof Array)){
13473                     return rs[selector];
13474                 }
13475                 for(var i = 0; i < selector.length; i++){
13476                         if(rs[selector[i]]){
13477                                 return rs[selector[i]];
13478                         }
13479                 }
13480                 return null;
13481         },
13482         
13483         
13484         /**
13485     * Updates a rule property
13486     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13487     * @param {String} property The css property
13488     * @param {String} value The new value for the property
13489     * @return {Boolean} true If a rule was found and updated
13490     */
13491    updateRule : function(selector, property, value){
13492                 if(!(selector instanceof Array)){
13493                         var rule = this.getRule(selector);
13494                         if(rule){
13495                                 rule.style[property.replace(camelRe, camelFn)] = value;
13496                                 return true;
13497                         }
13498                 }else{
13499                         for(var i = 0; i < selector.length; i++){
13500                                 if(this.updateRule(selector[i], property, value)){
13501                                         return true;
13502                                 }
13503                         }
13504                 }
13505                 return false;
13506         }
13507    };   
13508 }();/*
13509  * Based on:
13510  * Ext JS Library 1.1.1
13511  * Copyright(c) 2006-2007, Ext JS, LLC.
13512  *
13513  * Originally Released Under LGPL - original licence link has changed is not relivant.
13514  *
13515  * Fork - LGPL
13516  * <script type="text/javascript">
13517  */
13518
13519  
13520
13521 /**
13522  * @class Roo.util.ClickRepeater
13523  * @extends Roo.util.Observable
13524  * 
13525  * A wrapper class which can be applied to any element. Fires a "click" event while the
13526  * mouse is pressed. The interval between firings may be specified in the config but
13527  * defaults to 10 milliseconds.
13528  * 
13529  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13530  * 
13531  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13532  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13533  * Similar to an autorepeat key delay.
13534  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13535  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13536  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13537  *           "interval" and "delay" are ignored. "immediate" is honored.
13538  * @cfg {Boolean} preventDefault True to prevent the default click event
13539  * @cfg {Boolean} stopDefault True to stop the default click event
13540  * 
13541  * @history
13542  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13543  *     2007-02-02 jvs Renamed to ClickRepeater
13544  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13545  *
13546  *  @constructor
13547  * @param {String/HTMLElement/Element} el The element to listen on
13548  * @param {Object} config
13549  **/
13550 Roo.util.ClickRepeater = function(el, config)
13551 {
13552     this.el = Roo.get(el);
13553     this.el.unselectable();
13554
13555     Roo.apply(this, config);
13556
13557     this.addEvents({
13558     /**
13559      * @event mousedown
13560      * Fires when the mouse button is depressed.
13561      * @param {Roo.util.ClickRepeater} this
13562      */
13563         "mousedown" : true,
13564     /**
13565      * @event click
13566      * Fires on a specified interval during the time the element is pressed.
13567      * @param {Roo.util.ClickRepeater} this
13568      */
13569         "click" : true,
13570     /**
13571      * @event mouseup
13572      * Fires when the mouse key is released.
13573      * @param {Roo.util.ClickRepeater} this
13574      */
13575         "mouseup" : true
13576     });
13577
13578     this.el.on("mousedown", this.handleMouseDown, this);
13579     if(this.preventDefault || this.stopDefault){
13580         this.el.on("click", function(e){
13581             if(this.preventDefault){
13582                 e.preventDefault();
13583             }
13584             if(this.stopDefault){
13585                 e.stopEvent();
13586             }
13587         }, this);
13588     }
13589
13590     // allow inline handler
13591     if(this.handler){
13592         this.on("click", this.handler,  this.scope || this);
13593     }
13594
13595     Roo.util.ClickRepeater.superclass.constructor.call(this);
13596 };
13597
13598 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13599     interval : 20,
13600     delay: 250,
13601     preventDefault : true,
13602     stopDefault : false,
13603     timer : 0,
13604
13605     // private
13606     handleMouseDown : function(){
13607         clearTimeout(this.timer);
13608         this.el.blur();
13609         if(this.pressClass){
13610             this.el.addClass(this.pressClass);
13611         }
13612         this.mousedownTime = new Date();
13613
13614         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13615         this.el.on("mouseout", this.handleMouseOut, this);
13616
13617         this.fireEvent("mousedown", this);
13618         this.fireEvent("click", this);
13619         
13620         this.timer = this.click.defer(this.delay || this.interval, this);
13621     },
13622
13623     // private
13624     click : function(){
13625         this.fireEvent("click", this);
13626         this.timer = this.click.defer(this.getInterval(), this);
13627     },
13628
13629     // private
13630     getInterval: function(){
13631         if(!this.accelerate){
13632             return this.interval;
13633         }
13634         var pressTime = this.mousedownTime.getElapsed();
13635         if(pressTime < 500){
13636             return 400;
13637         }else if(pressTime < 1700){
13638             return 320;
13639         }else if(pressTime < 2600){
13640             return 250;
13641         }else if(pressTime < 3500){
13642             return 180;
13643         }else if(pressTime < 4400){
13644             return 140;
13645         }else if(pressTime < 5300){
13646             return 80;
13647         }else if(pressTime < 6200){
13648             return 50;
13649         }else{
13650             return 10;
13651         }
13652     },
13653
13654     // private
13655     handleMouseOut : function(){
13656         clearTimeout(this.timer);
13657         if(this.pressClass){
13658             this.el.removeClass(this.pressClass);
13659         }
13660         this.el.on("mouseover", this.handleMouseReturn, this);
13661     },
13662
13663     // private
13664     handleMouseReturn : function(){
13665         this.el.un("mouseover", this.handleMouseReturn);
13666         if(this.pressClass){
13667             this.el.addClass(this.pressClass);
13668         }
13669         this.click();
13670     },
13671
13672     // private
13673     handleMouseUp : function(){
13674         clearTimeout(this.timer);
13675         this.el.un("mouseover", this.handleMouseReturn);
13676         this.el.un("mouseout", this.handleMouseOut);
13677         Roo.get(document).un("mouseup", this.handleMouseUp);
13678         this.el.removeClass(this.pressClass);
13679         this.fireEvent("mouseup", this);
13680     }
13681 });/*
13682  * Based on:
13683  * Ext JS Library 1.1.1
13684  * Copyright(c) 2006-2007, Ext JS, LLC.
13685  *
13686  * Originally Released Under LGPL - original licence link has changed is not relivant.
13687  *
13688  * Fork - LGPL
13689  * <script type="text/javascript">
13690  */
13691
13692  
13693 /**
13694  * @class Roo.KeyNav
13695  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13696  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13697  * way to implement custom navigation schemes for any UI component.</p>
13698  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13699  * pageUp, pageDown, del, home, end.  Usage:</p>
13700  <pre><code>
13701 var nav = new Roo.KeyNav("my-element", {
13702     "left" : function(e){
13703         this.moveLeft(e.ctrlKey);
13704     },
13705     "right" : function(e){
13706         this.moveRight(e.ctrlKey);
13707     },
13708     "enter" : function(e){
13709         this.save();
13710     },
13711     scope : this
13712 });
13713 </code></pre>
13714  * @constructor
13715  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13716  * @param {Object} config The config
13717  */
13718 Roo.KeyNav = function(el, config){
13719     this.el = Roo.get(el);
13720     Roo.apply(this, config);
13721     if(!this.disabled){
13722         this.disabled = true;
13723         this.enable();
13724     }
13725 };
13726
13727 Roo.KeyNav.prototype = {
13728     /**
13729      * @cfg {Boolean} disabled
13730      * True to disable this KeyNav instance (defaults to false)
13731      */
13732     disabled : false,
13733     /**
13734      * @cfg {String} defaultEventAction
13735      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13736      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13737      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13738      */
13739     defaultEventAction: "stopEvent",
13740     /**
13741      * @cfg {Boolean} forceKeyDown
13742      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13743      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13744      * handle keydown instead of keypress.
13745      */
13746     forceKeyDown : false,
13747
13748     // private
13749     prepareEvent : function(e){
13750         var k = e.getKey();
13751         var h = this.keyToHandler[k];
13752         //if(h && this[h]){
13753         //    e.stopPropagation();
13754         //}
13755         if(Roo.isSafari && h && k >= 37 && k <= 40){
13756             e.stopEvent();
13757         }
13758     },
13759
13760     // private
13761     relay : function(e){
13762         var k = e.getKey();
13763         var h = this.keyToHandler[k];
13764         if(h && this[h]){
13765             if(this.doRelay(e, this[h], h) !== true){
13766                 e[this.defaultEventAction]();
13767             }
13768         }
13769     },
13770
13771     // private
13772     doRelay : function(e, h, hname){
13773         return h.call(this.scope || this, e);
13774     },
13775
13776     // possible handlers
13777     enter : false,
13778     left : false,
13779     right : false,
13780     up : false,
13781     down : false,
13782     tab : false,
13783     esc : false,
13784     pageUp : false,
13785     pageDown : false,
13786     del : false,
13787     home : false,
13788     end : false,
13789
13790     // quick lookup hash
13791     keyToHandler : {
13792         37 : "left",
13793         39 : "right",
13794         38 : "up",
13795         40 : "down",
13796         33 : "pageUp",
13797         34 : "pageDown",
13798         46 : "del",
13799         36 : "home",
13800         35 : "end",
13801         13 : "enter",
13802         27 : "esc",
13803         9  : "tab"
13804     },
13805
13806         /**
13807          * Enable this KeyNav
13808          */
13809         enable: function(){
13810                 if(this.disabled){
13811             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13812             // the EventObject will normalize Safari automatically
13813             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13814                 this.el.on("keydown", this.relay,  this);
13815             }else{
13816                 this.el.on("keydown", this.prepareEvent,  this);
13817                 this.el.on("keypress", this.relay,  this);
13818             }
13819                     this.disabled = false;
13820                 }
13821         },
13822
13823         /**
13824          * Disable this KeyNav
13825          */
13826         disable: function(){
13827                 if(!this.disabled){
13828                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13829                 this.el.un("keydown", this.relay);
13830             }else{
13831                 this.el.un("keydown", this.prepareEvent);
13832                 this.el.un("keypress", this.relay);
13833             }
13834                     this.disabled = true;
13835                 }
13836         }
13837 };/*
13838  * Based on:
13839  * Ext JS Library 1.1.1
13840  * Copyright(c) 2006-2007, Ext JS, LLC.
13841  *
13842  * Originally Released Under LGPL - original licence link has changed is not relivant.
13843  *
13844  * Fork - LGPL
13845  * <script type="text/javascript">
13846  */
13847
13848  
13849 /**
13850  * @class Roo.KeyMap
13851  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13852  * The constructor accepts the same config object as defined by {@link #addBinding}.
13853  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13854  * combination it will call the function with this signature (if the match is a multi-key
13855  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13856  * A KeyMap can also handle a string representation of keys.<br />
13857  * Usage:
13858  <pre><code>
13859 // map one key by key code
13860 var map = new Roo.KeyMap("my-element", {
13861     key: 13, // or Roo.EventObject.ENTER
13862     fn: myHandler,
13863     scope: myObject
13864 });
13865
13866 // map multiple keys to one action by string
13867 var map = new Roo.KeyMap("my-element", {
13868     key: "a\r\n\t",
13869     fn: myHandler,
13870     scope: myObject
13871 });
13872
13873 // map multiple keys to multiple actions by strings and array of codes
13874 var map = new Roo.KeyMap("my-element", [
13875     {
13876         key: [10,13],
13877         fn: function(){ alert("Return was pressed"); }
13878     }, {
13879         key: "abc",
13880         fn: function(){ alert('a, b or c was pressed'); }
13881     }, {
13882         key: "\t",
13883         ctrl:true,
13884         shift:true,
13885         fn: function(){ alert('Control + shift + tab was pressed.'); }
13886     }
13887 ]);
13888 </code></pre>
13889  * <b>Note: A KeyMap starts enabled</b>
13890  * @constructor
13891  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13892  * @param {Object} config The config (see {@link #addBinding})
13893  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13894  */
13895 Roo.KeyMap = function(el, config, eventName){
13896     this.el  = Roo.get(el);
13897     this.eventName = eventName || "keydown";
13898     this.bindings = [];
13899     if(config){
13900         this.addBinding(config);
13901     }
13902     this.enable();
13903 };
13904
13905 Roo.KeyMap.prototype = {
13906     /**
13907      * True to stop the event from bubbling and prevent the default browser action if the
13908      * key was handled by the KeyMap (defaults to false)
13909      * @type Boolean
13910      */
13911     stopEvent : false,
13912
13913     /**
13914      * Add a new binding to this KeyMap. The following config object properties are supported:
13915      * <pre>
13916 Property    Type             Description
13917 ----------  ---------------  ----------------------------------------------------------------------
13918 key         String/Array     A single keycode or an array of keycodes to handle
13919 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13920 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13921 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13922 fn          Function         The function to call when KeyMap finds the expected key combination
13923 scope       Object           The scope of the callback function
13924 </pre>
13925      *
13926      * Usage:
13927      * <pre><code>
13928 // Create a KeyMap
13929 var map = new Roo.KeyMap(document, {
13930     key: Roo.EventObject.ENTER,
13931     fn: handleKey,
13932     scope: this
13933 });
13934
13935 //Add a new binding to the existing KeyMap later
13936 map.addBinding({
13937     key: 'abc',
13938     shift: true,
13939     fn: handleKey,
13940     scope: this
13941 });
13942 </code></pre>
13943      * @param {Object/Array} config A single KeyMap config or an array of configs
13944      */
13945         addBinding : function(config){
13946         if(config instanceof Array){
13947             for(var i = 0, len = config.length; i < len; i++){
13948                 this.addBinding(config[i]);
13949             }
13950             return;
13951         }
13952         var keyCode = config.key,
13953             shift = config.shift, 
13954             ctrl = config.ctrl, 
13955             alt = config.alt,
13956             fn = config.fn,
13957             scope = config.scope;
13958         if(typeof keyCode == "string"){
13959             var ks = [];
13960             var keyString = keyCode.toUpperCase();
13961             for(var j = 0, len = keyString.length; j < len; j++){
13962                 ks.push(keyString.charCodeAt(j));
13963             }
13964             keyCode = ks;
13965         }
13966         var keyArray = keyCode instanceof Array;
13967         var handler = function(e){
13968             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13969                 var k = e.getKey();
13970                 if(keyArray){
13971                     for(var i = 0, len = keyCode.length; i < len; i++){
13972                         if(keyCode[i] == k){
13973                           if(this.stopEvent){
13974                               e.stopEvent();
13975                           }
13976                           fn.call(scope || window, k, e);
13977                           return;
13978                         }
13979                     }
13980                 }else{
13981                     if(k == keyCode){
13982                         if(this.stopEvent){
13983                            e.stopEvent();
13984                         }
13985                         fn.call(scope || window, k, e);
13986                     }
13987                 }
13988             }
13989         };
13990         this.bindings.push(handler);  
13991         },
13992
13993     /**
13994      * Shorthand for adding a single key listener
13995      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13996      * following options:
13997      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13998      * @param {Function} fn The function to call
13999      * @param {Object} scope (optional) The scope of the function
14000      */
14001     on : function(key, fn, scope){
14002         var keyCode, shift, ctrl, alt;
14003         if(typeof key == "object" && !(key instanceof Array)){
14004             keyCode = key.key;
14005             shift = key.shift;
14006             ctrl = key.ctrl;
14007             alt = key.alt;
14008         }else{
14009             keyCode = key;
14010         }
14011         this.addBinding({
14012             key: keyCode,
14013             shift: shift,
14014             ctrl: ctrl,
14015             alt: alt,
14016             fn: fn,
14017             scope: scope
14018         })
14019     },
14020
14021     // private
14022     handleKeyDown : function(e){
14023             if(this.enabled){ //just in case
14024             var b = this.bindings;
14025             for(var i = 0, len = b.length; i < len; i++){
14026                 b[i].call(this, e);
14027             }
14028             }
14029         },
14030         
14031         /**
14032          * Returns true if this KeyMap is enabled
14033          * @return {Boolean} 
14034          */
14035         isEnabled : function(){
14036             return this.enabled;  
14037         },
14038         
14039         /**
14040          * Enables this KeyMap
14041          */
14042         enable: function(){
14043                 if(!this.enabled){
14044                     this.el.on(this.eventName, this.handleKeyDown, this);
14045                     this.enabled = true;
14046                 }
14047         },
14048
14049         /**
14050          * Disable this KeyMap
14051          */
14052         disable: function(){
14053                 if(this.enabled){
14054                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14055                     this.enabled = false;
14056                 }
14057         }
14058 };/*
14059  * Based on:
14060  * Ext JS Library 1.1.1
14061  * Copyright(c) 2006-2007, Ext JS, LLC.
14062  *
14063  * Originally Released Under LGPL - original licence link has changed is not relivant.
14064  *
14065  * Fork - LGPL
14066  * <script type="text/javascript">
14067  */
14068
14069  
14070 /**
14071  * @class Roo.util.TextMetrics
14072  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14073  * wide, in pixels, a given block of text will be.
14074  * @singleton
14075  */
14076 Roo.util.TextMetrics = function(){
14077     var shared;
14078     return {
14079         /**
14080          * Measures the size of the specified text
14081          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14082          * that can affect the size of the rendered text
14083          * @param {String} text The text to measure
14084          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14085          * in order to accurately measure the text height
14086          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14087          */
14088         measure : function(el, text, fixedWidth){
14089             if(!shared){
14090                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14091             }
14092             shared.bind(el);
14093             shared.setFixedWidth(fixedWidth || 'auto');
14094             return shared.getSize(text);
14095         },
14096
14097         /**
14098          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14099          * the overhead of multiple calls to initialize the style properties on each measurement.
14100          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14101          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14102          * in order to accurately measure the text height
14103          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14104          */
14105         createInstance : function(el, fixedWidth){
14106             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14107         }
14108     };
14109 }();
14110
14111  
14112
14113 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14114     var ml = new Roo.Element(document.createElement('div'));
14115     document.body.appendChild(ml.dom);
14116     ml.position('absolute');
14117     ml.setLeftTop(-1000, -1000);
14118     ml.hide();
14119
14120     if(fixedWidth){
14121         ml.setWidth(fixedWidth);
14122     }
14123      
14124     var instance = {
14125         /**
14126          * Returns the size of the specified text based on the internal element's style and width properties
14127          * @memberOf Roo.util.TextMetrics.Instance#
14128          * @param {String} text The text to measure
14129          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14130          */
14131         getSize : function(text){
14132             ml.update(text);
14133             var s = ml.getSize();
14134             ml.update('');
14135             return s;
14136         },
14137
14138         /**
14139          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14140          * that can affect the size of the rendered text
14141          * @memberOf Roo.util.TextMetrics.Instance#
14142          * @param {String/HTMLElement} el The element, dom node or id
14143          */
14144         bind : function(el){
14145             ml.setStyle(
14146                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14147             );
14148         },
14149
14150         /**
14151          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14152          * to set a fixed width in order to accurately measure the text height.
14153          * @memberOf Roo.util.TextMetrics.Instance#
14154          * @param {Number} width The width to set on the element
14155          */
14156         setFixedWidth : function(width){
14157             ml.setWidth(width);
14158         },
14159
14160         /**
14161          * Returns the measured width of the specified text
14162          * @memberOf Roo.util.TextMetrics.Instance#
14163          * @param {String} text The text to measure
14164          * @return {Number} width The width in pixels
14165          */
14166         getWidth : function(text){
14167             ml.dom.style.width = 'auto';
14168             return this.getSize(text).width;
14169         },
14170
14171         /**
14172          * Returns the measured height of the specified text.  For multiline text, be sure to call
14173          * {@link #setFixedWidth} if necessary.
14174          * @memberOf Roo.util.TextMetrics.Instance#
14175          * @param {String} text The text to measure
14176          * @return {Number} height The height in pixels
14177          */
14178         getHeight : function(text){
14179             return this.getSize(text).height;
14180         }
14181     };
14182
14183     instance.bind(bindTo);
14184
14185     return instance;
14186 };
14187
14188 // backwards compat
14189 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14190  * Based on:
14191  * Ext JS Library 1.1.1
14192  * Copyright(c) 2006-2007, Ext JS, LLC.
14193  *
14194  * Originally Released Under LGPL - original licence link has changed is not relivant.
14195  *
14196  * Fork - LGPL
14197  * <script type="text/javascript">
14198  */
14199
14200 /**
14201  * @class Roo.state.Provider
14202  * Abstract base class for state provider implementations. This class provides methods
14203  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14204  * Provider interface.
14205  */
14206 Roo.state.Provider = function(){
14207     /**
14208      * @event statechange
14209      * Fires when a state change occurs.
14210      * @param {Provider} this This state provider
14211      * @param {String} key The state key which was changed
14212      * @param {String} value The encoded value for the state
14213      */
14214     this.addEvents({
14215         "statechange": true
14216     });
14217     this.state = {};
14218     Roo.state.Provider.superclass.constructor.call(this);
14219 };
14220 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14221     /**
14222      * Returns the current value for a key
14223      * @param {String} name The key name
14224      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14225      * @return {Mixed} The state data
14226      */
14227     get : function(name, defaultValue){
14228         return typeof this.state[name] == "undefined" ?
14229             defaultValue : this.state[name];
14230     },
14231     
14232     /**
14233      * Clears a value from the state
14234      * @param {String} name The key name
14235      */
14236     clear : function(name){
14237         delete this.state[name];
14238         this.fireEvent("statechange", this, name, null);
14239     },
14240     
14241     /**
14242      * Sets the value for a key
14243      * @param {String} name The key name
14244      * @param {Mixed} value The value to set
14245      */
14246     set : function(name, value){
14247         this.state[name] = value;
14248         this.fireEvent("statechange", this, name, value);
14249     },
14250     
14251     /**
14252      * Decodes a string previously encoded with {@link #encodeValue}.
14253      * @param {String} value The value to decode
14254      * @return {Mixed} The decoded value
14255      */
14256     decodeValue : function(cookie){
14257         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14258         var matches = re.exec(unescape(cookie));
14259         if(!matches || !matches[1]) return; // non state cookie
14260         var type = matches[1];
14261         var v = matches[2];
14262         switch(type){
14263             case "n":
14264                 return parseFloat(v);
14265             case "d":
14266                 return new Date(Date.parse(v));
14267             case "b":
14268                 return (v == "1");
14269             case "a":
14270                 var all = [];
14271                 var values = v.split("^");
14272                 for(var i = 0, len = values.length; i < len; i++){
14273                     all.push(this.decodeValue(values[i]));
14274                 }
14275                 return all;
14276            case "o":
14277                 var all = {};
14278                 var values = v.split("^");
14279                 for(var i = 0, len = values.length; i < len; i++){
14280                     var kv = values[i].split("=");
14281                     all[kv[0]] = this.decodeValue(kv[1]);
14282                 }
14283                 return all;
14284            default:
14285                 return v;
14286         }
14287     },
14288     
14289     /**
14290      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14291      * @param {Mixed} value The value to encode
14292      * @return {String} The encoded value
14293      */
14294     encodeValue : function(v){
14295         var enc;
14296         if(typeof v == "number"){
14297             enc = "n:" + v;
14298         }else if(typeof v == "boolean"){
14299             enc = "b:" + (v ? "1" : "0");
14300         }else if(v instanceof Date){
14301             enc = "d:" + v.toGMTString();
14302         }else if(v instanceof Array){
14303             var flat = "";
14304             for(var i = 0, len = v.length; i < len; i++){
14305                 flat += this.encodeValue(v[i]);
14306                 if(i != len-1) flat += "^";
14307             }
14308             enc = "a:" + flat;
14309         }else if(typeof v == "object"){
14310             var flat = "";
14311             for(var key in v){
14312                 if(typeof v[key] != "function"){
14313                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14314                 }
14315             }
14316             enc = "o:" + flat.substring(0, flat.length-1);
14317         }else{
14318             enc = "s:" + v;
14319         }
14320         return escape(enc);        
14321     }
14322 });
14323
14324 /*
14325  * Based on:
14326  * Ext JS Library 1.1.1
14327  * Copyright(c) 2006-2007, Ext JS, LLC.
14328  *
14329  * Originally Released Under LGPL - original licence link has changed is not relivant.
14330  *
14331  * Fork - LGPL
14332  * <script type="text/javascript">
14333  */
14334 /**
14335  * @class Roo.state.Manager
14336  * This is the global state manager. By default all components that are "state aware" check this class
14337  * for state information if you don't pass them a custom state provider. In order for this class
14338  * to be useful, it must be initialized with a provider when your application initializes.
14339  <pre><code>
14340 // in your initialization function
14341 init : function(){
14342    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14343    ...
14344    // supposed you have a {@link Roo.BorderLayout}
14345    var layout = new Roo.BorderLayout(...);
14346    layout.restoreState();
14347    // or a {Roo.BasicDialog}
14348    var dialog = new Roo.BasicDialog(...);
14349    dialog.restoreState();
14350  </code></pre>
14351  * @singleton
14352  */
14353 Roo.state.Manager = function(){
14354     var provider = new Roo.state.Provider();
14355     
14356     return {
14357         /**
14358          * Configures the default state provider for your application
14359          * @param {Provider} stateProvider The state provider to set
14360          */
14361         setProvider : function(stateProvider){
14362             provider = stateProvider;
14363         },
14364         
14365         /**
14366          * Returns the current value for a key
14367          * @param {String} name The key name
14368          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14369          * @return {Mixed} The state data
14370          */
14371         get : function(key, defaultValue){
14372             return provider.get(key, defaultValue);
14373         },
14374         
14375         /**
14376          * Sets the value for a key
14377          * @param {String} name The key name
14378          * @param {Mixed} value The state data
14379          */
14380          set : function(key, value){
14381             provider.set(key, value);
14382         },
14383         
14384         /**
14385          * Clears a value from the state
14386          * @param {String} name The key name
14387          */
14388         clear : function(key){
14389             provider.clear(key);
14390         },
14391         
14392         /**
14393          * Gets the currently configured state provider
14394          * @return {Provider} The state provider
14395          */
14396         getProvider : function(){
14397             return provider;
14398         }
14399     };
14400 }();
14401 /*
14402  * Based on:
14403  * Ext JS Library 1.1.1
14404  * Copyright(c) 2006-2007, Ext JS, LLC.
14405  *
14406  * Originally Released Under LGPL - original licence link has changed is not relivant.
14407  *
14408  * Fork - LGPL
14409  * <script type="text/javascript">
14410  */
14411 /**
14412  * @class Roo.state.CookieProvider
14413  * @extends Roo.state.Provider
14414  * The default Provider implementation which saves state via cookies.
14415  * <br />Usage:
14416  <pre><code>
14417    var cp = new Roo.state.CookieProvider({
14418        path: "/cgi-bin/",
14419        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14420        domain: "roojs.com"
14421    })
14422    Roo.state.Manager.setProvider(cp);
14423  </code></pre>
14424  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14425  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14426  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14427  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14428  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14429  * domain the page is running on including the 'www' like 'www.roojs.com')
14430  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14431  * @constructor
14432  * Create a new CookieProvider
14433  * @param {Object} config The configuration object
14434  */
14435 Roo.state.CookieProvider = function(config){
14436     Roo.state.CookieProvider.superclass.constructor.call(this);
14437     this.path = "/";
14438     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14439     this.domain = null;
14440     this.secure = false;
14441     Roo.apply(this, config);
14442     this.state = this.readCookies();
14443 };
14444
14445 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14446     // private
14447     set : function(name, value){
14448         if(typeof value == "undefined" || value === null){
14449             this.clear(name);
14450             return;
14451         }
14452         this.setCookie(name, value);
14453         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14454     },
14455
14456     // private
14457     clear : function(name){
14458         this.clearCookie(name);
14459         Roo.state.CookieProvider.superclass.clear.call(this, name);
14460     },
14461
14462     // private
14463     readCookies : function(){
14464         var cookies = {};
14465         var c = document.cookie + ";";
14466         var re = /\s?(.*?)=(.*?);/g;
14467         var matches;
14468         while((matches = re.exec(c)) != null){
14469             var name = matches[1];
14470             var value = matches[2];
14471             if(name && name.substring(0,3) == "ys-"){
14472                 cookies[name.substr(3)] = this.decodeValue(value);
14473             }
14474         }
14475         return cookies;
14476     },
14477
14478     // private
14479     setCookie : function(name, value){
14480         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14481            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14482            ((this.path == null) ? "" : ("; path=" + this.path)) +
14483            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14484            ((this.secure == true) ? "; secure" : "");
14485     },
14486
14487     // private
14488     clearCookie : function(name){
14489         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14490            ((this.path == null) ? "" : ("; path=" + this.path)) +
14491            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14492            ((this.secure == true) ? "; secure" : "");
14493     }
14494 });/*
14495  * Based on:
14496  * Ext JS Library 1.1.1
14497  * Copyright(c) 2006-2007, Ext JS, LLC.
14498  *
14499  * Originally Released Under LGPL - original licence link has changed is not relivant.
14500  *
14501  * Fork - LGPL
14502  * <script type="text/javascript">
14503  */
14504
14505
14506
14507 /*
14508  * These classes are derivatives of the similarly named classes in the YUI Library.
14509  * The original license:
14510  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14511  * Code licensed under the BSD License:
14512  * http://developer.yahoo.net/yui/license.txt
14513  */
14514
14515 (function() {
14516
14517 var Event=Roo.EventManager;
14518 var Dom=Roo.lib.Dom;
14519
14520 /**
14521  * @class Roo.dd.DragDrop
14522  * @extends Roo.util.Observable
14523  * Defines the interface and base operation of items that that can be
14524  * dragged or can be drop targets.  It was designed to be extended, overriding
14525  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14526  * Up to three html elements can be associated with a DragDrop instance:
14527  * <ul>
14528  * <li>linked element: the element that is passed into the constructor.
14529  * This is the element which defines the boundaries for interaction with
14530  * other DragDrop objects.</li>
14531  * <li>handle element(s): The drag operation only occurs if the element that
14532  * was clicked matches a handle element.  By default this is the linked
14533  * element, but there are times that you will want only a portion of the
14534  * linked element to initiate the drag operation, and the setHandleElId()
14535  * method provides a way to define this.</li>
14536  * <li>drag element: this represents the element that would be moved along
14537  * with the cursor during a drag operation.  By default, this is the linked
14538  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14539  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14540  * </li>
14541  * </ul>
14542  * This class should not be instantiated until the onload event to ensure that
14543  * the associated elements are available.
14544  * The following would define a DragDrop obj that would interact with any
14545  * other DragDrop obj in the "group1" group:
14546  * <pre>
14547  *  dd = new Roo.dd.DragDrop("div1", "group1");
14548  * </pre>
14549  * Since none of the event handlers have been implemented, nothing would
14550  * actually happen if you were to run the code above.  Normally you would
14551  * override this class or one of the default implementations, but you can
14552  * also override the methods you want on an instance of the class...
14553  * <pre>
14554  *  dd.onDragDrop = function(e, id) {
14555  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14556  *  }
14557  * </pre>
14558  * @constructor
14559  * @param {String} id of the element that is linked to this instance
14560  * @param {String} sGroup the group of related DragDrop objects
14561  * @param {object} config an object containing configurable attributes
14562  *                Valid properties for DragDrop:
14563  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14564  */
14565 Roo.dd.DragDrop = function(id, sGroup, config) {
14566     if (id) {
14567         this.init(id, sGroup, config);
14568     }
14569     
14570 };
14571
14572 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14573
14574     /**
14575      * The id of the element associated with this object.  This is what we
14576      * refer to as the "linked element" because the size and position of
14577      * this element is used to determine when the drag and drop objects have
14578      * interacted.
14579      * @property id
14580      * @type String
14581      */
14582     id: null,
14583
14584     /**
14585      * Configuration attributes passed into the constructor
14586      * @property config
14587      * @type object
14588      */
14589     config: null,
14590
14591     /**
14592      * The id of the element that will be dragged.  By default this is same
14593      * as the linked element , but could be changed to another element. Ex:
14594      * Roo.dd.DDProxy
14595      * @property dragElId
14596      * @type String
14597      * @private
14598      */
14599     dragElId: null,
14600
14601     /**
14602      * the id of the element that initiates the drag operation.  By default
14603      * this is the linked element, but could be changed to be a child of this
14604      * element.  This lets us do things like only starting the drag when the
14605      * header element within the linked html element is clicked.
14606      * @property handleElId
14607      * @type String
14608      * @private
14609      */
14610     handleElId: null,
14611
14612     /**
14613      * An associative array of HTML tags that will be ignored if clicked.
14614      * @property invalidHandleTypes
14615      * @type {string: string}
14616      */
14617     invalidHandleTypes: null,
14618
14619     /**
14620      * An associative array of ids for elements that will be ignored if clicked
14621      * @property invalidHandleIds
14622      * @type {string: string}
14623      */
14624     invalidHandleIds: null,
14625
14626     /**
14627      * An indexted array of css class names for elements that will be ignored
14628      * if clicked.
14629      * @property invalidHandleClasses
14630      * @type string[]
14631      */
14632     invalidHandleClasses: null,
14633
14634     /**
14635      * The linked element's absolute X position at the time the drag was
14636      * started
14637      * @property startPageX
14638      * @type int
14639      * @private
14640      */
14641     startPageX: 0,
14642
14643     /**
14644      * The linked element's absolute X position at the time the drag was
14645      * started
14646      * @property startPageY
14647      * @type int
14648      * @private
14649      */
14650     startPageY: 0,
14651
14652     /**
14653      * The group defines a logical collection of DragDrop objects that are
14654      * related.  Instances only get events when interacting with other
14655      * DragDrop object in the same group.  This lets us define multiple
14656      * groups using a single DragDrop subclass if we want.
14657      * @property groups
14658      * @type {string: string}
14659      */
14660     groups: null,
14661
14662     /**
14663      * Individual drag/drop instances can be locked.  This will prevent
14664      * onmousedown start drag.
14665      * @property locked
14666      * @type boolean
14667      * @private
14668      */
14669     locked: false,
14670
14671     /**
14672      * Lock this instance
14673      * @method lock
14674      */
14675     lock: function() { this.locked = true; },
14676
14677     /**
14678      * Unlock this instace
14679      * @method unlock
14680      */
14681     unlock: function() { this.locked = false; },
14682
14683     /**
14684      * By default, all insances can be a drop target.  This can be disabled by
14685      * setting isTarget to false.
14686      * @method isTarget
14687      * @type boolean
14688      */
14689     isTarget: true,
14690
14691     /**
14692      * The padding configured for this drag and drop object for calculating
14693      * the drop zone intersection with this object.
14694      * @method padding
14695      * @type int[]
14696      */
14697     padding: null,
14698
14699     /**
14700      * Cached reference to the linked element
14701      * @property _domRef
14702      * @private
14703      */
14704     _domRef: null,
14705
14706     /**
14707      * Internal typeof flag
14708      * @property __ygDragDrop
14709      * @private
14710      */
14711     __ygDragDrop: true,
14712
14713     /**
14714      * Set to true when horizontal contraints are applied
14715      * @property constrainX
14716      * @type boolean
14717      * @private
14718      */
14719     constrainX: false,
14720
14721     /**
14722      * Set to true when vertical contraints are applied
14723      * @property constrainY
14724      * @type boolean
14725      * @private
14726      */
14727     constrainY: false,
14728
14729     /**
14730      * The left constraint
14731      * @property minX
14732      * @type int
14733      * @private
14734      */
14735     minX: 0,
14736
14737     /**
14738      * The right constraint
14739      * @property maxX
14740      * @type int
14741      * @private
14742      */
14743     maxX: 0,
14744
14745     /**
14746      * The up constraint
14747      * @property minY
14748      * @type int
14749      * @type int
14750      * @private
14751      */
14752     minY: 0,
14753
14754     /**
14755      * The down constraint
14756      * @property maxY
14757      * @type int
14758      * @private
14759      */
14760     maxY: 0,
14761
14762     /**
14763      * Maintain offsets when we resetconstraints.  Set to true when you want
14764      * the position of the element relative to its parent to stay the same
14765      * when the page changes
14766      *
14767      * @property maintainOffset
14768      * @type boolean
14769      */
14770     maintainOffset: false,
14771
14772     /**
14773      * Array of pixel locations the element will snap to if we specified a
14774      * horizontal graduation/interval.  This array is generated automatically
14775      * when you define a tick interval.
14776      * @property xTicks
14777      * @type int[]
14778      */
14779     xTicks: null,
14780
14781     /**
14782      * Array of pixel locations the element will snap to if we specified a
14783      * vertical graduation/interval.  This array is generated automatically
14784      * when you define a tick interval.
14785      * @property yTicks
14786      * @type int[]
14787      */
14788     yTicks: null,
14789
14790     /**
14791      * By default the drag and drop instance will only respond to the primary
14792      * button click (left button for a right-handed mouse).  Set to true to
14793      * allow drag and drop to start with any mouse click that is propogated
14794      * by the browser
14795      * @property primaryButtonOnly
14796      * @type boolean
14797      */
14798     primaryButtonOnly: true,
14799
14800     /**
14801      * The availabe property is false until the linked dom element is accessible.
14802      * @property available
14803      * @type boolean
14804      */
14805     available: false,
14806
14807     /**
14808      * By default, drags can only be initiated if the mousedown occurs in the
14809      * region the linked element is.  This is done in part to work around a
14810      * bug in some browsers that mis-report the mousedown if the previous
14811      * mouseup happened outside of the window.  This property is set to true
14812      * if outer handles are defined.
14813      *
14814      * @property hasOuterHandles
14815      * @type boolean
14816      * @default false
14817      */
14818     hasOuterHandles: false,
14819
14820     /**
14821      * Code that executes immediately before the startDrag event
14822      * @method b4StartDrag
14823      * @private
14824      */
14825     b4StartDrag: function(x, y) { },
14826
14827     /**
14828      * Abstract method called after a drag/drop object is clicked
14829      * and the drag or mousedown time thresholds have beeen met.
14830      * @method startDrag
14831      * @param {int} X click location
14832      * @param {int} Y click location
14833      */
14834     startDrag: function(x, y) { /* override this */ },
14835
14836     /**
14837      * Code that executes immediately before the onDrag event
14838      * @method b4Drag
14839      * @private
14840      */
14841     b4Drag: function(e) { },
14842
14843     /**
14844      * Abstract method called during the onMouseMove event while dragging an
14845      * object.
14846      * @method onDrag
14847      * @param {Event} e the mousemove event
14848      */
14849     onDrag: function(e) { /* override this */ },
14850
14851     /**
14852      * Abstract method called when this element fist begins hovering over
14853      * another DragDrop obj
14854      * @method onDragEnter
14855      * @param {Event} e the mousemove event
14856      * @param {String|DragDrop[]} id In POINT mode, the element
14857      * id this is hovering over.  In INTERSECT mode, an array of one or more
14858      * dragdrop items being hovered over.
14859      */
14860     onDragEnter: function(e, id) { /* override this */ },
14861
14862     /**
14863      * Code that executes immediately before the onDragOver event
14864      * @method b4DragOver
14865      * @private
14866      */
14867     b4DragOver: function(e) { },
14868
14869     /**
14870      * Abstract method called when this element is hovering over another
14871      * DragDrop obj
14872      * @method onDragOver
14873      * @param {Event} e the mousemove event
14874      * @param {String|DragDrop[]} id In POINT mode, the element
14875      * id this is hovering over.  In INTERSECT mode, an array of dd items
14876      * being hovered over.
14877      */
14878     onDragOver: function(e, id) { /* override this */ },
14879
14880     /**
14881      * Code that executes immediately before the onDragOut event
14882      * @method b4DragOut
14883      * @private
14884      */
14885     b4DragOut: function(e) { },
14886
14887     /**
14888      * Abstract method called when we are no longer hovering over an element
14889      * @method onDragOut
14890      * @param {Event} e the mousemove event
14891      * @param {String|DragDrop[]} id In POINT mode, the element
14892      * id this was hovering over.  In INTERSECT mode, an array of dd items
14893      * that the mouse is no longer over.
14894      */
14895     onDragOut: function(e, id) { /* override this */ },
14896
14897     /**
14898      * Code that executes immediately before the onDragDrop event
14899      * @method b4DragDrop
14900      * @private
14901      */
14902     b4DragDrop: function(e) { },
14903
14904     /**
14905      * Abstract method called when this item is dropped on another DragDrop
14906      * obj
14907      * @method onDragDrop
14908      * @param {Event} e the mouseup event
14909      * @param {String|DragDrop[]} id In POINT mode, the element
14910      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14911      * was dropped on.
14912      */
14913     onDragDrop: function(e, id) { /* override this */ },
14914
14915     /**
14916      * Abstract method called when this item is dropped on an area with no
14917      * drop target
14918      * @method onInvalidDrop
14919      * @param {Event} e the mouseup event
14920      */
14921     onInvalidDrop: function(e) { /* override this */ },
14922
14923     /**
14924      * Code that executes immediately before the endDrag event
14925      * @method b4EndDrag
14926      * @private
14927      */
14928     b4EndDrag: function(e) { },
14929
14930     /**
14931      * Fired when we are done dragging the object
14932      * @method endDrag
14933      * @param {Event} e the mouseup event
14934      */
14935     endDrag: function(e) { /* override this */ },
14936
14937     /**
14938      * Code executed immediately before the onMouseDown event
14939      * @method b4MouseDown
14940      * @param {Event} e the mousedown event
14941      * @private
14942      */
14943     b4MouseDown: function(e) {  },
14944
14945     /**
14946      * Event handler that fires when a drag/drop obj gets a mousedown
14947      * @method onMouseDown
14948      * @param {Event} e the mousedown event
14949      */
14950     onMouseDown: function(e) { /* override this */ },
14951
14952     /**
14953      * Event handler that fires when a drag/drop obj gets a mouseup
14954      * @method onMouseUp
14955      * @param {Event} e the mouseup event
14956      */
14957     onMouseUp: function(e) { /* override this */ },
14958
14959     /**
14960      * Override the onAvailable method to do what is needed after the initial
14961      * position was determined.
14962      * @method onAvailable
14963      */
14964     onAvailable: function () {
14965     },
14966
14967     /*
14968      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14969      * @type Object
14970      */
14971     defaultPadding : {left:0, right:0, top:0, bottom:0},
14972
14973     /*
14974      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14975  *
14976  * Usage:
14977  <pre><code>
14978  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14979                 { dragElId: "existingProxyDiv" });
14980  dd.startDrag = function(){
14981      this.constrainTo("parent-id");
14982  };
14983  </code></pre>
14984  * Or you can initalize it using the {@link Roo.Element} object:
14985  <pre><code>
14986  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14987      startDrag : function(){
14988          this.constrainTo("parent-id");
14989      }
14990  });
14991  </code></pre>
14992      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14993      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14994      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14995      * an object containing the sides to pad. For example: {right:10, bottom:10}
14996      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14997      */
14998     constrainTo : function(constrainTo, pad, inContent){
14999         if(typeof pad == "number"){
15000             pad = {left: pad, right:pad, top:pad, bottom:pad};
15001         }
15002         pad = pad || this.defaultPadding;
15003         var b = Roo.get(this.getEl()).getBox();
15004         var ce = Roo.get(constrainTo);
15005         var s = ce.getScroll();
15006         var c, cd = ce.dom;
15007         if(cd == document.body){
15008             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
15009         }else{
15010             xy = ce.getXY();
15011             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
15012         }
15013
15014
15015         var topSpace = b.y - c.y;
15016         var leftSpace = b.x - c.x;
15017
15018         this.resetConstraints();
15019         this.setXConstraint(leftSpace - (pad.left||0), // left
15020                 c.width - leftSpace - b.width - (pad.right||0) //right
15021         );
15022         this.setYConstraint(topSpace - (pad.top||0), //top
15023                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
15024         );
15025     },
15026
15027     /**
15028      * Returns a reference to the linked element
15029      * @method getEl
15030      * @return {HTMLElement} the html element
15031      */
15032     getEl: function() {
15033         if (!this._domRef) {
15034             this._domRef = Roo.getDom(this.id);
15035         }
15036
15037         return this._domRef;
15038     },
15039
15040     /**
15041      * Returns a reference to the actual element to drag.  By default this is
15042      * the same as the html element, but it can be assigned to another
15043      * element. An example of this can be found in Roo.dd.DDProxy
15044      * @method getDragEl
15045      * @return {HTMLElement} the html element
15046      */
15047     getDragEl: function() {
15048         return Roo.getDom(this.dragElId);
15049     },
15050
15051     /**
15052      * Sets up the DragDrop object.  Must be called in the constructor of any
15053      * Roo.dd.DragDrop subclass
15054      * @method init
15055      * @param id the id of the linked element
15056      * @param {String} sGroup the group of related items
15057      * @param {object} config configuration attributes
15058      */
15059     init: function(id, sGroup, config) {
15060         this.initTarget(id, sGroup, config);
15061         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15062         // Event.on(this.id, "selectstart", Event.preventDefault);
15063     },
15064
15065     /**
15066      * Initializes Targeting functionality only... the object does not
15067      * get a mousedown handler.
15068      * @method initTarget
15069      * @param id the id of the linked element
15070      * @param {String} sGroup the group of related items
15071      * @param {object} config configuration attributes
15072      */
15073     initTarget: function(id, sGroup, config) {
15074
15075         // configuration attributes
15076         this.config = config || {};
15077
15078         // create a local reference to the drag and drop manager
15079         this.DDM = Roo.dd.DDM;
15080         // initialize the groups array
15081         this.groups = {};
15082
15083         // assume that we have an element reference instead of an id if the
15084         // parameter is not a string
15085         if (typeof id !== "string") {
15086             id = Roo.id(id);
15087         }
15088
15089         // set the id
15090         this.id = id;
15091
15092         // add to an interaction group
15093         this.addToGroup((sGroup) ? sGroup : "default");
15094
15095         // We don't want to register this as the handle with the manager
15096         // so we just set the id rather than calling the setter.
15097         this.handleElId = id;
15098
15099         // the linked element is the element that gets dragged by default
15100         this.setDragElId(id);
15101
15102         // by default, clicked anchors will not start drag operations.
15103         this.invalidHandleTypes = { A: "A" };
15104         this.invalidHandleIds = {};
15105         this.invalidHandleClasses = [];
15106
15107         this.applyConfig();
15108
15109         this.handleOnAvailable();
15110     },
15111
15112     /**
15113      * Applies the configuration parameters that were passed into the constructor.
15114      * This is supposed to happen at each level through the inheritance chain.  So
15115      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15116      * DragDrop in order to get all of the parameters that are available in
15117      * each object.
15118      * @method applyConfig
15119      */
15120     applyConfig: function() {
15121
15122         // configurable properties:
15123         //    padding, isTarget, maintainOffset, primaryButtonOnly
15124         this.padding           = this.config.padding || [0, 0, 0, 0];
15125         this.isTarget          = (this.config.isTarget !== false);
15126         this.maintainOffset    = (this.config.maintainOffset);
15127         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15128
15129     },
15130
15131     /**
15132      * Executed when the linked element is available
15133      * @method handleOnAvailable
15134      * @private
15135      */
15136     handleOnAvailable: function() {
15137         this.available = true;
15138         this.resetConstraints();
15139         this.onAvailable();
15140     },
15141
15142      /**
15143      * Configures the padding for the target zone in px.  Effectively expands
15144      * (or reduces) the virtual object size for targeting calculations.
15145      * Supports css-style shorthand; if only one parameter is passed, all sides
15146      * will have that padding, and if only two are passed, the top and bottom
15147      * will have the first param, the left and right the second.
15148      * @method setPadding
15149      * @param {int} iTop    Top pad
15150      * @param {int} iRight  Right pad
15151      * @param {int} iBot    Bot pad
15152      * @param {int} iLeft   Left pad
15153      */
15154     setPadding: function(iTop, iRight, iBot, iLeft) {
15155         // this.padding = [iLeft, iRight, iTop, iBot];
15156         if (!iRight && 0 !== iRight) {
15157             this.padding = [iTop, iTop, iTop, iTop];
15158         } else if (!iBot && 0 !== iBot) {
15159             this.padding = [iTop, iRight, iTop, iRight];
15160         } else {
15161             this.padding = [iTop, iRight, iBot, iLeft];
15162         }
15163     },
15164
15165     /**
15166      * Stores the initial placement of the linked element.
15167      * @method setInitialPosition
15168      * @param {int} diffX   the X offset, default 0
15169      * @param {int} diffY   the Y offset, default 0
15170      */
15171     setInitPosition: function(diffX, diffY) {
15172         var el = this.getEl();
15173
15174         if (!this.DDM.verifyEl(el)) {
15175             return;
15176         }
15177
15178         var dx = diffX || 0;
15179         var dy = diffY || 0;
15180
15181         var p = Dom.getXY( el );
15182
15183         this.initPageX = p[0] - dx;
15184         this.initPageY = p[1] - dy;
15185
15186         this.lastPageX = p[0];
15187         this.lastPageY = p[1];
15188
15189
15190         this.setStartPosition(p);
15191     },
15192
15193     /**
15194      * Sets the start position of the element.  This is set when the obj
15195      * is initialized, the reset when a drag is started.
15196      * @method setStartPosition
15197      * @param pos current position (from previous lookup)
15198      * @private
15199      */
15200     setStartPosition: function(pos) {
15201         var p = pos || Dom.getXY( this.getEl() );
15202         this.deltaSetXY = null;
15203
15204         this.startPageX = p[0];
15205         this.startPageY = p[1];
15206     },
15207
15208     /**
15209      * Add this instance to a group of related drag/drop objects.  All
15210      * instances belong to at least one group, and can belong to as many
15211      * groups as needed.
15212      * @method addToGroup
15213      * @param sGroup {string} the name of the group
15214      */
15215     addToGroup: function(sGroup) {
15216         this.groups[sGroup] = true;
15217         this.DDM.regDragDrop(this, sGroup);
15218     },
15219
15220     /**
15221      * Remove's this instance from the supplied interaction group
15222      * @method removeFromGroup
15223      * @param {string}  sGroup  The group to drop
15224      */
15225     removeFromGroup: function(sGroup) {
15226         if (this.groups[sGroup]) {
15227             delete this.groups[sGroup];
15228         }
15229
15230         this.DDM.removeDDFromGroup(this, sGroup);
15231     },
15232
15233     /**
15234      * Allows you to specify that an element other than the linked element
15235      * will be moved with the cursor during a drag
15236      * @method setDragElId
15237      * @param id {string} the id of the element that will be used to initiate the drag
15238      */
15239     setDragElId: function(id) {
15240         this.dragElId = id;
15241     },
15242
15243     /**
15244      * Allows you to specify a child of the linked element that should be
15245      * used to initiate the drag operation.  An example of this would be if
15246      * you have a content div with text and links.  Clicking anywhere in the
15247      * content area would normally start the drag operation.  Use this method
15248      * to specify that an element inside of the content div is the element
15249      * that starts the drag operation.
15250      * @method setHandleElId
15251      * @param id {string} the id of the element that will be used to
15252      * initiate the drag.
15253      */
15254     setHandleElId: function(id) {
15255         if (typeof id !== "string") {
15256             id = Roo.id(id);
15257         }
15258         this.handleElId = id;
15259         this.DDM.regHandle(this.id, id);
15260     },
15261
15262     /**
15263      * Allows you to set an element outside of the linked element as a drag
15264      * handle
15265      * @method setOuterHandleElId
15266      * @param id the id of the element that will be used to initiate the drag
15267      */
15268     setOuterHandleElId: function(id) {
15269         if (typeof id !== "string") {
15270             id = Roo.id(id);
15271         }
15272         Event.on(id, "mousedown",
15273                 this.handleMouseDown, this);
15274         this.setHandleElId(id);
15275
15276         this.hasOuterHandles = true;
15277     },
15278
15279     /**
15280      * Remove all drag and drop hooks for this element
15281      * @method unreg
15282      */
15283     unreg: function() {
15284         Event.un(this.id, "mousedown",
15285                 this.handleMouseDown);
15286         this._domRef = null;
15287         this.DDM._remove(this);
15288     },
15289
15290     destroy : function(){
15291         this.unreg();
15292     },
15293
15294     /**
15295      * Returns true if this instance is locked, or the drag drop mgr is locked
15296      * (meaning that all drag/drop is disabled on the page.)
15297      * @method isLocked
15298      * @return {boolean} true if this obj or all drag/drop is locked, else
15299      * false
15300      */
15301     isLocked: function() {
15302         return (this.DDM.isLocked() || this.locked);
15303     },
15304
15305     /**
15306      * Fired when this object is clicked
15307      * @method handleMouseDown
15308      * @param {Event} e
15309      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15310      * @private
15311      */
15312     handleMouseDown: function(e, oDD){
15313         if (this.primaryButtonOnly && e.button != 0) {
15314             return;
15315         }
15316
15317         if (this.isLocked()) {
15318             return;
15319         }
15320
15321         this.DDM.refreshCache(this.groups);
15322
15323         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15324         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15325         } else {
15326             if (this.clickValidator(e)) {
15327
15328                 // set the initial element position
15329                 this.setStartPosition();
15330
15331
15332                 this.b4MouseDown(e);
15333                 this.onMouseDown(e);
15334
15335                 this.DDM.handleMouseDown(e, this);
15336
15337                 this.DDM.stopEvent(e);
15338             } else {
15339
15340
15341             }
15342         }
15343     },
15344
15345     clickValidator: function(e) {
15346         var target = e.getTarget();
15347         return ( this.isValidHandleChild(target) &&
15348                     (this.id == this.handleElId ||
15349                         this.DDM.handleWasClicked(target, this.id)) );
15350     },
15351
15352     /**
15353      * Allows you to specify a tag name that should not start a drag operation
15354      * when clicked.  This is designed to facilitate embedding links within a
15355      * drag handle that do something other than start the drag.
15356      * @method addInvalidHandleType
15357      * @param {string} tagName the type of element to exclude
15358      */
15359     addInvalidHandleType: function(tagName) {
15360         var type = tagName.toUpperCase();
15361         this.invalidHandleTypes[type] = type;
15362     },
15363
15364     /**
15365      * Lets you to specify an element id for a child of a drag handle
15366      * that should not initiate a drag
15367      * @method addInvalidHandleId
15368      * @param {string} id the element id of the element you wish to ignore
15369      */
15370     addInvalidHandleId: function(id) {
15371         if (typeof id !== "string") {
15372             id = Roo.id(id);
15373         }
15374         this.invalidHandleIds[id] = id;
15375     },
15376
15377     /**
15378      * Lets you specify a css class of elements that will not initiate a drag
15379      * @method addInvalidHandleClass
15380      * @param {string} cssClass the class of the elements you wish to ignore
15381      */
15382     addInvalidHandleClass: function(cssClass) {
15383         this.invalidHandleClasses.push(cssClass);
15384     },
15385
15386     /**
15387      * Unsets an excluded tag name set by addInvalidHandleType
15388      * @method removeInvalidHandleType
15389      * @param {string} tagName the type of element to unexclude
15390      */
15391     removeInvalidHandleType: function(tagName) {
15392         var type = tagName.toUpperCase();
15393         // this.invalidHandleTypes[type] = null;
15394         delete this.invalidHandleTypes[type];
15395     },
15396
15397     /**
15398      * Unsets an invalid handle id
15399      * @method removeInvalidHandleId
15400      * @param {string} id the id of the element to re-enable
15401      */
15402     removeInvalidHandleId: function(id) {
15403         if (typeof id !== "string") {
15404             id = Roo.id(id);
15405         }
15406         delete this.invalidHandleIds[id];
15407     },
15408
15409     /**
15410      * Unsets an invalid css class
15411      * @method removeInvalidHandleClass
15412      * @param {string} cssClass the class of the element(s) you wish to
15413      * re-enable
15414      */
15415     removeInvalidHandleClass: function(cssClass) {
15416         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15417             if (this.invalidHandleClasses[i] == cssClass) {
15418                 delete this.invalidHandleClasses[i];
15419             }
15420         }
15421     },
15422
15423     /**
15424      * Checks the tag exclusion list to see if this click should be ignored
15425      * @method isValidHandleChild
15426      * @param {HTMLElement} node the HTMLElement to evaluate
15427      * @return {boolean} true if this is a valid tag type, false if not
15428      */
15429     isValidHandleChild: function(node) {
15430
15431         var valid = true;
15432         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15433         var nodeName;
15434         try {
15435             nodeName = node.nodeName.toUpperCase();
15436         } catch(e) {
15437             nodeName = node.nodeName;
15438         }
15439         valid = valid && !this.invalidHandleTypes[nodeName];
15440         valid = valid && !this.invalidHandleIds[node.id];
15441
15442         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15443             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15444         }
15445
15446
15447         return valid;
15448
15449     },
15450
15451     /**
15452      * Create the array of horizontal tick marks if an interval was specified
15453      * in setXConstraint().
15454      * @method setXTicks
15455      * @private
15456      */
15457     setXTicks: function(iStartX, iTickSize) {
15458         this.xTicks = [];
15459         this.xTickSize = iTickSize;
15460
15461         var tickMap = {};
15462
15463         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15464             if (!tickMap[i]) {
15465                 this.xTicks[this.xTicks.length] = i;
15466                 tickMap[i] = true;
15467             }
15468         }
15469
15470         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15471             if (!tickMap[i]) {
15472                 this.xTicks[this.xTicks.length] = i;
15473                 tickMap[i] = true;
15474             }
15475         }
15476
15477         this.xTicks.sort(this.DDM.numericSort) ;
15478     },
15479
15480     /**
15481      * Create the array of vertical tick marks if an interval was specified in
15482      * setYConstraint().
15483      * @method setYTicks
15484      * @private
15485      */
15486     setYTicks: function(iStartY, iTickSize) {
15487         this.yTicks = [];
15488         this.yTickSize = iTickSize;
15489
15490         var tickMap = {};
15491
15492         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15493             if (!tickMap[i]) {
15494                 this.yTicks[this.yTicks.length] = i;
15495                 tickMap[i] = true;
15496             }
15497         }
15498
15499         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15500             if (!tickMap[i]) {
15501                 this.yTicks[this.yTicks.length] = i;
15502                 tickMap[i] = true;
15503             }
15504         }
15505
15506         this.yTicks.sort(this.DDM.numericSort) ;
15507     },
15508
15509     /**
15510      * By default, the element can be dragged any place on the screen.  Use
15511      * this method to limit the horizontal travel of the element.  Pass in
15512      * 0,0 for the parameters if you want to lock the drag to the y axis.
15513      * @method setXConstraint
15514      * @param {int} iLeft the number of pixels the element can move to the left
15515      * @param {int} iRight the number of pixels the element can move to the
15516      * right
15517      * @param {int} iTickSize optional parameter for specifying that the
15518      * element
15519      * should move iTickSize pixels at a time.
15520      */
15521     setXConstraint: function(iLeft, iRight, iTickSize) {
15522         this.leftConstraint = iLeft;
15523         this.rightConstraint = iRight;
15524
15525         this.minX = this.initPageX - iLeft;
15526         this.maxX = this.initPageX + iRight;
15527         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15528
15529         this.constrainX = true;
15530     },
15531
15532     /**
15533      * Clears any constraints applied to this instance.  Also clears ticks
15534      * since they can't exist independent of a constraint at this time.
15535      * @method clearConstraints
15536      */
15537     clearConstraints: function() {
15538         this.constrainX = false;
15539         this.constrainY = false;
15540         this.clearTicks();
15541     },
15542
15543     /**
15544      * Clears any tick interval defined for this instance
15545      * @method clearTicks
15546      */
15547     clearTicks: function() {
15548         this.xTicks = null;
15549         this.yTicks = null;
15550         this.xTickSize = 0;
15551         this.yTickSize = 0;
15552     },
15553
15554     /**
15555      * By default, the element can be dragged any place on the screen.  Set
15556      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15557      * parameters if you want to lock the drag to the x axis.
15558      * @method setYConstraint
15559      * @param {int} iUp the number of pixels the element can move up
15560      * @param {int} iDown the number of pixels the element can move down
15561      * @param {int} iTickSize optional parameter for specifying that the
15562      * element should move iTickSize pixels at a time.
15563      */
15564     setYConstraint: function(iUp, iDown, iTickSize) {
15565         this.topConstraint = iUp;
15566         this.bottomConstraint = iDown;
15567
15568         this.minY = this.initPageY - iUp;
15569         this.maxY = this.initPageY + iDown;
15570         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15571
15572         this.constrainY = true;
15573
15574     },
15575
15576     /**
15577      * resetConstraints must be called if you manually reposition a dd element.
15578      * @method resetConstraints
15579      * @param {boolean} maintainOffset
15580      */
15581     resetConstraints: function() {
15582
15583
15584         // Maintain offsets if necessary
15585         if (this.initPageX || this.initPageX === 0) {
15586             // figure out how much this thing has moved
15587             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15588             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15589
15590             this.setInitPosition(dx, dy);
15591
15592         // This is the first time we have detected the element's position
15593         } else {
15594             this.setInitPosition();
15595         }
15596
15597         if (this.constrainX) {
15598             this.setXConstraint( this.leftConstraint,
15599                                  this.rightConstraint,
15600                                  this.xTickSize        );
15601         }
15602
15603         if (this.constrainY) {
15604             this.setYConstraint( this.topConstraint,
15605                                  this.bottomConstraint,
15606                                  this.yTickSize         );
15607         }
15608     },
15609
15610     /**
15611      * Normally the drag element is moved pixel by pixel, but we can specify
15612      * that it move a number of pixels at a time.  This method resolves the
15613      * location when we have it set up like this.
15614      * @method getTick
15615      * @param {int} val where we want to place the object
15616      * @param {int[]} tickArray sorted array of valid points
15617      * @return {int} the closest tick
15618      * @private
15619      */
15620     getTick: function(val, tickArray) {
15621
15622         if (!tickArray) {
15623             // If tick interval is not defined, it is effectively 1 pixel,
15624             // so we return the value passed to us.
15625             return val;
15626         } else if (tickArray[0] >= val) {
15627             // The value is lower than the first tick, so we return the first
15628             // tick.
15629             return tickArray[0];
15630         } else {
15631             for (var i=0, len=tickArray.length; i<len; ++i) {
15632                 var next = i + 1;
15633                 if (tickArray[next] && tickArray[next] >= val) {
15634                     var diff1 = val - tickArray[i];
15635                     var diff2 = tickArray[next] - val;
15636                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15637                 }
15638             }
15639
15640             // The value is larger than the last tick, so we return the last
15641             // tick.
15642             return tickArray[tickArray.length - 1];
15643         }
15644     },
15645
15646     /**
15647      * toString method
15648      * @method toString
15649      * @return {string} string representation of the dd obj
15650      */
15651     toString: function() {
15652         return ("DragDrop " + this.id);
15653     }
15654
15655 });
15656
15657 })();
15658 /*
15659  * Based on:
15660  * Ext JS Library 1.1.1
15661  * Copyright(c) 2006-2007, Ext JS, LLC.
15662  *
15663  * Originally Released Under LGPL - original licence link has changed is not relivant.
15664  *
15665  * Fork - LGPL
15666  * <script type="text/javascript">
15667  */
15668
15669
15670 /**
15671  * The drag and drop utility provides a framework for building drag and drop
15672  * applications.  In addition to enabling drag and drop for specific elements,
15673  * the drag and drop elements are tracked by the manager class, and the
15674  * interactions between the various elements are tracked during the drag and
15675  * the implementing code is notified about these important moments.
15676  */
15677
15678 // Only load the library once.  Rewriting the manager class would orphan
15679 // existing drag and drop instances.
15680 if (!Roo.dd.DragDropMgr) {
15681
15682 /**
15683  * @class Roo.dd.DragDropMgr
15684  * DragDropMgr is a singleton that tracks the element interaction for
15685  * all DragDrop items in the window.  Generally, you will not call
15686  * this class directly, but it does have helper methods that could
15687  * be useful in your DragDrop implementations.
15688  * @singleton
15689  */
15690 Roo.dd.DragDropMgr = function() {
15691
15692     var Event = Roo.EventManager;
15693
15694     return {
15695
15696         /**
15697          * Two dimensional Array of registered DragDrop objects.  The first
15698          * dimension is the DragDrop item group, the second the DragDrop
15699          * object.
15700          * @property ids
15701          * @type {string: string}
15702          * @private
15703          * @static
15704          */
15705         ids: {},
15706
15707         /**
15708          * Array of element ids defined as drag handles.  Used to determine
15709          * if the element that generated the mousedown event is actually the
15710          * handle and not the html element itself.
15711          * @property handleIds
15712          * @type {string: string}
15713          * @private
15714          * @static
15715          */
15716         handleIds: {},
15717
15718         /**
15719          * the DragDrop object that is currently being dragged
15720          * @property dragCurrent
15721          * @type DragDrop
15722          * @private
15723          * @static
15724          **/
15725         dragCurrent: null,
15726
15727         /**
15728          * the DragDrop object(s) that are being hovered over
15729          * @property dragOvers
15730          * @type Array
15731          * @private
15732          * @static
15733          */
15734         dragOvers: {},
15735
15736         /**
15737          * the X distance between the cursor and the object being dragged
15738          * @property deltaX
15739          * @type int
15740          * @private
15741          * @static
15742          */
15743         deltaX: 0,
15744
15745         /**
15746          * the Y distance between the cursor and the object being dragged
15747          * @property deltaY
15748          * @type int
15749          * @private
15750          * @static
15751          */
15752         deltaY: 0,
15753
15754         /**
15755          * Flag to determine if we should prevent the default behavior of the
15756          * events we define. By default this is true, but this can be set to
15757          * false if you need the default behavior (not recommended)
15758          * @property preventDefault
15759          * @type boolean
15760          * @static
15761          */
15762         preventDefault: true,
15763
15764         /**
15765          * Flag to determine if we should stop the propagation of the events
15766          * we generate. This is true by default but you may want to set it to
15767          * false if the html element contains other features that require the
15768          * mouse click.
15769          * @property stopPropagation
15770          * @type boolean
15771          * @static
15772          */
15773         stopPropagation: true,
15774
15775         /**
15776          * Internal flag that is set to true when drag and drop has been
15777          * intialized
15778          * @property initialized
15779          * @private
15780          * @static
15781          */
15782         initalized: false,
15783
15784         /**
15785          * All drag and drop can be disabled.
15786          * @property locked
15787          * @private
15788          * @static
15789          */
15790         locked: false,
15791
15792         /**
15793          * Called the first time an element is registered.
15794          * @method init
15795          * @private
15796          * @static
15797          */
15798         init: function() {
15799             this.initialized = true;
15800         },
15801
15802         /**
15803          * In point mode, drag and drop interaction is defined by the
15804          * location of the cursor during the drag/drop
15805          * @property POINT
15806          * @type int
15807          * @static
15808          */
15809         POINT: 0,
15810
15811         /**
15812          * In intersect mode, drag and drop interactio nis defined by the
15813          * overlap of two or more drag and drop objects.
15814          * @property INTERSECT
15815          * @type int
15816          * @static
15817          */
15818         INTERSECT: 1,
15819
15820         /**
15821          * The current drag and drop mode.  Default: POINT
15822          * @property mode
15823          * @type int
15824          * @static
15825          */
15826         mode: 0,
15827
15828         /**
15829          * Runs method on all drag and drop objects
15830          * @method _execOnAll
15831          * @private
15832          * @static
15833          */
15834         _execOnAll: function(sMethod, args) {
15835             for (var i in this.ids) {
15836                 for (var j in this.ids[i]) {
15837                     var oDD = this.ids[i][j];
15838                     if (! this.isTypeOfDD(oDD)) {
15839                         continue;
15840                     }
15841                     oDD[sMethod].apply(oDD, args);
15842                 }
15843             }
15844         },
15845
15846         /**
15847          * Drag and drop initialization.  Sets up the global event handlers
15848          * @method _onLoad
15849          * @private
15850          * @static
15851          */
15852         _onLoad: function() {
15853
15854             this.init();
15855
15856
15857             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15858             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15859             Event.on(window,   "unload",    this._onUnload, this, true);
15860             Event.on(window,   "resize",    this._onResize, this, true);
15861             // Event.on(window,   "mouseout",    this._test);
15862
15863         },
15864
15865         /**
15866          * Reset constraints on all drag and drop objs
15867          * @method _onResize
15868          * @private
15869          * @static
15870          */
15871         _onResize: function(e) {
15872             this._execOnAll("resetConstraints", []);
15873         },
15874
15875         /**
15876          * Lock all drag and drop functionality
15877          * @method lock
15878          * @static
15879          */
15880         lock: function() { this.locked = true; },
15881
15882         /**
15883          * Unlock all drag and drop functionality
15884          * @method unlock
15885          * @static
15886          */
15887         unlock: function() { this.locked = false; },
15888
15889         /**
15890          * Is drag and drop locked?
15891          * @method isLocked
15892          * @return {boolean} True if drag and drop is locked, false otherwise.
15893          * @static
15894          */
15895         isLocked: function() { return this.locked; },
15896
15897         /**
15898          * Location cache that is set for all drag drop objects when a drag is
15899          * initiated, cleared when the drag is finished.
15900          * @property locationCache
15901          * @private
15902          * @static
15903          */
15904         locationCache: {},
15905
15906         /**
15907          * Set useCache to false if you want to force object the lookup of each
15908          * drag and drop linked element constantly during a drag.
15909          * @property useCache
15910          * @type boolean
15911          * @static
15912          */
15913         useCache: true,
15914
15915         /**
15916          * The number of pixels that the mouse needs to move after the
15917          * mousedown before the drag is initiated.  Default=3;
15918          * @property clickPixelThresh
15919          * @type int
15920          * @static
15921          */
15922         clickPixelThresh: 3,
15923
15924         /**
15925          * The number of milliseconds after the mousedown event to initiate the
15926          * drag if we don't get a mouseup event. Default=1000
15927          * @property clickTimeThresh
15928          * @type int
15929          * @static
15930          */
15931         clickTimeThresh: 350,
15932
15933         /**
15934          * Flag that indicates that either the drag pixel threshold or the
15935          * mousdown time threshold has been met
15936          * @property dragThreshMet
15937          * @type boolean
15938          * @private
15939          * @static
15940          */
15941         dragThreshMet: false,
15942
15943         /**
15944          * Timeout used for the click time threshold
15945          * @property clickTimeout
15946          * @type Object
15947          * @private
15948          * @static
15949          */
15950         clickTimeout: null,
15951
15952         /**
15953          * The X position of the mousedown event stored for later use when a
15954          * drag threshold is met.
15955          * @property startX
15956          * @type int
15957          * @private
15958          * @static
15959          */
15960         startX: 0,
15961
15962         /**
15963          * The Y position of the mousedown event stored for later use when a
15964          * drag threshold is met.
15965          * @property startY
15966          * @type int
15967          * @private
15968          * @static
15969          */
15970         startY: 0,
15971
15972         /**
15973          * Each DragDrop instance must be registered with the DragDropMgr.
15974          * This is executed in DragDrop.init()
15975          * @method regDragDrop
15976          * @param {DragDrop} oDD the DragDrop object to register
15977          * @param {String} sGroup the name of the group this element belongs to
15978          * @static
15979          */
15980         regDragDrop: function(oDD, sGroup) {
15981             if (!this.initialized) { this.init(); }
15982
15983             if (!this.ids[sGroup]) {
15984                 this.ids[sGroup] = {};
15985             }
15986             this.ids[sGroup][oDD.id] = oDD;
15987         },
15988
15989         /**
15990          * Removes the supplied dd instance from the supplied group. Executed
15991          * by DragDrop.removeFromGroup, so don't call this function directly.
15992          * @method removeDDFromGroup
15993          * @private
15994          * @static
15995          */
15996         removeDDFromGroup: function(oDD, sGroup) {
15997             if (!this.ids[sGroup]) {
15998                 this.ids[sGroup] = {};
15999             }
16000
16001             var obj = this.ids[sGroup];
16002             if (obj && obj[oDD.id]) {
16003                 delete obj[oDD.id];
16004             }
16005         },
16006
16007         /**
16008          * Unregisters a drag and drop item.  This is executed in
16009          * DragDrop.unreg, use that method instead of calling this directly.
16010          * @method _remove
16011          * @private
16012          * @static
16013          */
16014         _remove: function(oDD) {
16015             for (var g in oDD.groups) {
16016                 if (g && this.ids[g][oDD.id]) {
16017                     delete this.ids[g][oDD.id];
16018                 }
16019             }
16020             delete this.handleIds[oDD.id];
16021         },
16022
16023         /**
16024          * Each DragDrop handle element must be registered.  This is done
16025          * automatically when executing DragDrop.setHandleElId()
16026          * @method regHandle
16027          * @param {String} sDDId the DragDrop id this element is a handle for
16028          * @param {String} sHandleId the id of the element that is the drag
16029          * handle
16030          * @static
16031          */
16032         regHandle: function(sDDId, sHandleId) {
16033             if (!this.handleIds[sDDId]) {
16034                 this.handleIds[sDDId] = {};
16035             }
16036             this.handleIds[sDDId][sHandleId] = sHandleId;
16037         },
16038
16039         /**
16040          * Utility function to determine if a given element has been
16041          * registered as a drag drop item.
16042          * @method isDragDrop
16043          * @param {String} id the element id to check
16044          * @return {boolean} true if this element is a DragDrop item,
16045          * false otherwise
16046          * @static
16047          */
16048         isDragDrop: function(id) {
16049             return ( this.getDDById(id) ) ? true : false;
16050         },
16051
16052         /**
16053          * Returns the drag and drop instances that are in all groups the
16054          * passed in instance belongs to.
16055          * @method getRelated
16056          * @param {DragDrop} p_oDD the obj to get related data for
16057          * @param {boolean} bTargetsOnly if true, only return targetable objs
16058          * @return {DragDrop[]} the related instances
16059          * @static
16060          */
16061         getRelated: function(p_oDD, bTargetsOnly) {
16062             var oDDs = [];
16063             for (var i in p_oDD.groups) {
16064                 for (j in this.ids[i]) {
16065                     var dd = this.ids[i][j];
16066                     if (! this.isTypeOfDD(dd)) {
16067                         continue;
16068                     }
16069                     if (!bTargetsOnly || dd.isTarget) {
16070                         oDDs[oDDs.length] = dd;
16071                     }
16072                 }
16073             }
16074
16075             return oDDs;
16076         },
16077
16078         /**
16079          * Returns true if the specified dd target is a legal target for
16080          * the specifice drag obj
16081          * @method isLegalTarget
16082          * @param {DragDrop} the drag obj
16083          * @param {DragDrop} the target
16084          * @return {boolean} true if the target is a legal target for the
16085          * dd obj
16086          * @static
16087          */
16088         isLegalTarget: function (oDD, oTargetDD) {
16089             var targets = this.getRelated(oDD, true);
16090             for (var i=0, len=targets.length;i<len;++i) {
16091                 if (targets[i].id == oTargetDD.id) {
16092                     return true;
16093                 }
16094             }
16095
16096             return false;
16097         },
16098
16099         /**
16100          * My goal is to be able to transparently determine if an object is
16101          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16102          * returns "object", oDD.constructor.toString() always returns
16103          * "DragDrop" and not the name of the subclass.  So for now it just
16104          * evaluates a well-known variable in DragDrop.
16105          * @method isTypeOfDD
16106          * @param {Object} the object to evaluate
16107          * @return {boolean} true if typeof oDD = DragDrop
16108          * @static
16109          */
16110         isTypeOfDD: function (oDD) {
16111             return (oDD && oDD.__ygDragDrop);
16112         },
16113
16114         /**
16115          * Utility function to determine if a given element has been
16116          * registered as a drag drop handle for the given Drag Drop object.
16117          * @method isHandle
16118          * @param {String} id the element id to check
16119          * @return {boolean} true if this element is a DragDrop handle, false
16120          * otherwise
16121          * @static
16122          */
16123         isHandle: function(sDDId, sHandleId) {
16124             return ( this.handleIds[sDDId] &&
16125                             this.handleIds[sDDId][sHandleId] );
16126         },
16127
16128         /**
16129          * Returns the DragDrop instance for a given id
16130          * @method getDDById
16131          * @param {String} id the id of the DragDrop object
16132          * @return {DragDrop} the drag drop object, null if it is not found
16133          * @static
16134          */
16135         getDDById: function(id) {
16136             for (var i in this.ids) {
16137                 if (this.ids[i][id]) {
16138                     return this.ids[i][id];
16139                 }
16140             }
16141             return null;
16142         },
16143
16144         /**
16145          * Fired after a registered DragDrop object gets the mousedown event.
16146          * Sets up the events required to track the object being dragged
16147          * @method handleMouseDown
16148          * @param {Event} e the event
16149          * @param oDD the DragDrop object being dragged
16150          * @private
16151          * @static
16152          */
16153         handleMouseDown: function(e, oDD) {
16154             if(Roo.QuickTips){
16155                 Roo.QuickTips.disable();
16156             }
16157             this.currentTarget = e.getTarget();
16158
16159             this.dragCurrent = oDD;
16160
16161             var el = oDD.getEl();
16162
16163             // track start position
16164             this.startX = e.getPageX();
16165             this.startY = e.getPageY();
16166
16167             this.deltaX = this.startX - el.offsetLeft;
16168             this.deltaY = this.startY - el.offsetTop;
16169
16170             this.dragThreshMet = false;
16171
16172             this.clickTimeout = setTimeout(
16173                     function() {
16174                         var DDM = Roo.dd.DDM;
16175                         DDM.startDrag(DDM.startX, DDM.startY);
16176                     },
16177                     this.clickTimeThresh );
16178         },
16179
16180         /**
16181          * Fired when either the drag pixel threshol or the mousedown hold
16182          * time threshold has been met.
16183          * @method startDrag
16184          * @param x {int} the X position of the original mousedown
16185          * @param y {int} the Y position of the original mousedown
16186          * @static
16187          */
16188         startDrag: function(x, y) {
16189             clearTimeout(this.clickTimeout);
16190             if (this.dragCurrent) {
16191                 this.dragCurrent.b4StartDrag(x, y);
16192                 this.dragCurrent.startDrag(x, y);
16193             }
16194             this.dragThreshMet = true;
16195         },
16196
16197         /**
16198          * Internal function to handle the mouseup event.  Will be invoked
16199          * from the context of the document.
16200          * @method handleMouseUp
16201          * @param {Event} e the event
16202          * @private
16203          * @static
16204          */
16205         handleMouseUp: function(e) {
16206
16207             if(Roo.QuickTips){
16208                 Roo.QuickTips.enable();
16209             }
16210             if (! this.dragCurrent) {
16211                 return;
16212             }
16213
16214             clearTimeout(this.clickTimeout);
16215
16216             if (this.dragThreshMet) {
16217                 this.fireEvents(e, true);
16218             } else {
16219             }
16220
16221             this.stopDrag(e);
16222
16223             this.stopEvent(e);
16224         },
16225
16226         /**
16227          * Utility to stop event propagation and event default, if these
16228          * features are turned on.
16229          * @method stopEvent
16230          * @param {Event} e the event as returned by this.getEvent()
16231          * @static
16232          */
16233         stopEvent: function(e){
16234             if(this.stopPropagation) {
16235                 e.stopPropagation();
16236             }
16237
16238             if (this.preventDefault) {
16239                 e.preventDefault();
16240             }
16241         },
16242
16243         /**
16244          * Internal function to clean up event handlers after the drag
16245          * operation is complete
16246          * @method stopDrag
16247          * @param {Event} e the event
16248          * @private
16249          * @static
16250          */
16251         stopDrag: function(e) {
16252             // Fire the drag end event for the item that was dragged
16253             if (this.dragCurrent) {
16254                 if (this.dragThreshMet) {
16255                     this.dragCurrent.b4EndDrag(e);
16256                     this.dragCurrent.endDrag(e);
16257                 }
16258
16259                 this.dragCurrent.onMouseUp(e);
16260             }
16261
16262             this.dragCurrent = null;
16263             this.dragOvers = {};
16264         },
16265
16266         /**
16267          * Internal function to handle the mousemove event.  Will be invoked
16268          * from the context of the html element.
16269          *
16270          * @TODO figure out what we can do about mouse events lost when the
16271          * user drags objects beyond the window boundary.  Currently we can
16272          * detect this in internet explorer by verifying that the mouse is
16273          * down during the mousemove event.  Firefox doesn't give us the
16274          * button state on the mousemove event.
16275          * @method handleMouseMove
16276          * @param {Event} e the event
16277          * @private
16278          * @static
16279          */
16280         handleMouseMove: function(e) {
16281             if (! this.dragCurrent) {
16282                 return true;
16283             }
16284
16285             // var button = e.which || e.button;
16286
16287             // check for IE mouseup outside of page boundary
16288             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16289                 this.stopEvent(e);
16290                 return this.handleMouseUp(e);
16291             }
16292
16293             if (!this.dragThreshMet) {
16294                 var diffX = Math.abs(this.startX - e.getPageX());
16295                 var diffY = Math.abs(this.startY - e.getPageY());
16296                 if (diffX > this.clickPixelThresh ||
16297                             diffY > this.clickPixelThresh) {
16298                     this.startDrag(this.startX, this.startY);
16299                 }
16300             }
16301
16302             if (this.dragThreshMet) {
16303                 this.dragCurrent.b4Drag(e);
16304                 this.dragCurrent.onDrag(e);
16305                 if(!this.dragCurrent.moveOnly){
16306                     this.fireEvents(e, false);
16307                 }
16308             }
16309
16310             this.stopEvent(e);
16311
16312             return true;
16313         },
16314
16315         /**
16316          * Iterates over all of the DragDrop elements to find ones we are
16317          * hovering over or dropping on
16318          * @method fireEvents
16319          * @param {Event} e the event
16320          * @param {boolean} isDrop is this a drop op or a mouseover op?
16321          * @private
16322          * @static
16323          */
16324         fireEvents: function(e, isDrop) {
16325             var dc = this.dragCurrent;
16326
16327             // If the user did the mouse up outside of the window, we could
16328             // get here even though we have ended the drag.
16329             if (!dc || dc.isLocked()) {
16330                 return;
16331             }
16332
16333             var pt = e.getPoint();
16334
16335             // cache the previous dragOver array
16336             var oldOvers = [];
16337
16338             var outEvts   = [];
16339             var overEvts  = [];
16340             var dropEvts  = [];
16341             var enterEvts = [];
16342
16343             // Check to see if the object(s) we were hovering over is no longer
16344             // being hovered over so we can fire the onDragOut event
16345             for (var i in this.dragOvers) {
16346
16347                 var ddo = this.dragOvers[i];
16348
16349                 if (! this.isTypeOfDD(ddo)) {
16350                     continue;
16351                 }
16352
16353                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16354                     outEvts.push( ddo );
16355                 }
16356
16357                 oldOvers[i] = true;
16358                 delete this.dragOvers[i];
16359             }
16360
16361             for (var sGroup in dc.groups) {
16362
16363                 if ("string" != typeof sGroup) {
16364                     continue;
16365                 }
16366
16367                 for (i in this.ids[sGroup]) {
16368                     var oDD = this.ids[sGroup][i];
16369                     if (! this.isTypeOfDD(oDD)) {
16370                         continue;
16371                     }
16372
16373                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16374                         if (this.isOverTarget(pt, oDD, this.mode)) {
16375                             // look for drop interactions
16376                             if (isDrop) {
16377                                 dropEvts.push( oDD );
16378                             // look for drag enter and drag over interactions
16379                             } else {
16380
16381                                 // initial drag over: dragEnter fires
16382                                 if (!oldOvers[oDD.id]) {
16383                                     enterEvts.push( oDD );
16384                                 // subsequent drag overs: dragOver fires
16385                                 } else {
16386                                     overEvts.push( oDD );
16387                                 }
16388
16389                                 this.dragOvers[oDD.id] = oDD;
16390                             }
16391                         }
16392                     }
16393                 }
16394             }
16395
16396             if (this.mode) {
16397                 if (outEvts.length) {
16398                     dc.b4DragOut(e, outEvts);
16399                     dc.onDragOut(e, outEvts);
16400                 }
16401
16402                 if (enterEvts.length) {
16403                     dc.onDragEnter(e, enterEvts);
16404                 }
16405
16406                 if (overEvts.length) {
16407                     dc.b4DragOver(e, overEvts);
16408                     dc.onDragOver(e, overEvts);
16409                 }
16410
16411                 if (dropEvts.length) {
16412                     dc.b4DragDrop(e, dropEvts);
16413                     dc.onDragDrop(e, dropEvts);
16414                 }
16415
16416             } else {
16417                 // fire dragout events
16418                 var len = 0;
16419                 for (i=0, len=outEvts.length; i<len; ++i) {
16420                     dc.b4DragOut(e, outEvts[i].id);
16421                     dc.onDragOut(e, outEvts[i].id);
16422                 }
16423
16424                 // fire enter events
16425                 for (i=0,len=enterEvts.length; i<len; ++i) {
16426                     // dc.b4DragEnter(e, oDD.id);
16427                     dc.onDragEnter(e, enterEvts[i].id);
16428                 }
16429
16430                 // fire over events
16431                 for (i=0,len=overEvts.length; i<len; ++i) {
16432                     dc.b4DragOver(e, overEvts[i].id);
16433                     dc.onDragOver(e, overEvts[i].id);
16434                 }
16435
16436                 // fire drop events
16437                 for (i=0, len=dropEvts.length; i<len; ++i) {
16438                     dc.b4DragDrop(e, dropEvts[i].id);
16439                     dc.onDragDrop(e, dropEvts[i].id);
16440                 }
16441
16442             }
16443
16444             // notify about a drop that did not find a target
16445             if (isDrop && !dropEvts.length) {
16446                 dc.onInvalidDrop(e);
16447             }
16448
16449         },
16450
16451         /**
16452          * Helper function for getting the best match from the list of drag
16453          * and drop objects returned by the drag and drop events when we are
16454          * in INTERSECT mode.  It returns either the first object that the
16455          * cursor is over, or the object that has the greatest overlap with
16456          * the dragged element.
16457          * @method getBestMatch
16458          * @param  {DragDrop[]} dds The array of drag and drop objects
16459          * targeted
16460          * @return {DragDrop}       The best single match
16461          * @static
16462          */
16463         getBestMatch: function(dds) {
16464             var winner = null;
16465             // Return null if the input is not what we expect
16466             //if (!dds || !dds.length || dds.length == 0) {
16467                // winner = null;
16468             // If there is only one item, it wins
16469             //} else if (dds.length == 1) {
16470
16471             var len = dds.length;
16472
16473             if (len == 1) {
16474                 winner = dds[0];
16475             } else {
16476                 // Loop through the targeted items
16477                 for (var i=0; i<len; ++i) {
16478                     var dd = dds[i];
16479                     // If the cursor is over the object, it wins.  If the
16480                     // cursor is over multiple matches, the first one we come
16481                     // to wins.
16482                     if (dd.cursorIsOver) {
16483                         winner = dd;
16484                         break;
16485                     // Otherwise the object with the most overlap wins
16486                     } else {
16487                         if (!winner ||
16488                             winner.overlap.getArea() < dd.overlap.getArea()) {
16489                             winner = dd;
16490                         }
16491                     }
16492                 }
16493             }
16494
16495             return winner;
16496         },
16497
16498         /**
16499          * Refreshes the cache of the top-left and bottom-right points of the
16500          * drag and drop objects in the specified group(s).  This is in the
16501          * format that is stored in the drag and drop instance, so typical
16502          * usage is:
16503          * <code>
16504          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16505          * </code>
16506          * Alternatively:
16507          * <code>
16508          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16509          * </code>
16510          * @TODO this really should be an indexed array.  Alternatively this
16511          * method could accept both.
16512          * @method refreshCache
16513          * @param {Object} groups an associative array of groups to refresh
16514          * @static
16515          */
16516         refreshCache: function(groups) {
16517             for (var sGroup in groups) {
16518                 if ("string" != typeof sGroup) {
16519                     continue;
16520                 }
16521                 for (var i in this.ids[sGroup]) {
16522                     var oDD = this.ids[sGroup][i];
16523
16524                     if (this.isTypeOfDD(oDD)) {
16525                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16526                         var loc = this.getLocation(oDD);
16527                         if (loc) {
16528                             this.locationCache[oDD.id] = loc;
16529                         } else {
16530                             delete this.locationCache[oDD.id];
16531                             // this will unregister the drag and drop object if
16532                             // the element is not in a usable state
16533                             // oDD.unreg();
16534                         }
16535                     }
16536                 }
16537             }
16538         },
16539
16540         /**
16541          * This checks to make sure an element exists and is in the DOM.  The
16542          * main purpose is to handle cases where innerHTML is used to remove
16543          * drag and drop objects from the DOM.  IE provides an 'unspecified
16544          * error' when trying to access the offsetParent of such an element
16545          * @method verifyEl
16546          * @param {HTMLElement} el the element to check
16547          * @return {boolean} true if the element looks usable
16548          * @static
16549          */
16550         verifyEl: function(el) {
16551             if (el) {
16552                 var parent;
16553                 if(Roo.isIE){
16554                     try{
16555                         parent = el.offsetParent;
16556                     }catch(e){}
16557                 }else{
16558                     parent = el.offsetParent;
16559                 }
16560                 if (parent) {
16561                     return true;
16562                 }
16563             }
16564
16565             return false;
16566         },
16567
16568         /**
16569          * Returns a Region object containing the drag and drop element's position
16570          * and size, including the padding configured for it
16571          * @method getLocation
16572          * @param {DragDrop} oDD the drag and drop object to get the
16573          *                       location for
16574          * @return {Roo.lib.Region} a Region object representing the total area
16575          *                             the element occupies, including any padding
16576          *                             the instance is configured for.
16577          * @static
16578          */
16579         getLocation: function(oDD) {
16580             if (! this.isTypeOfDD(oDD)) {
16581                 return null;
16582             }
16583
16584             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16585
16586             try {
16587                 pos= Roo.lib.Dom.getXY(el);
16588             } catch (e) { }
16589
16590             if (!pos) {
16591                 return null;
16592             }
16593
16594             x1 = pos[0];
16595             x2 = x1 + el.offsetWidth;
16596             y1 = pos[1];
16597             y2 = y1 + el.offsetHeight;
16598
16599             t = y1 - oDD.padding[0];
16600             r = x2 + oDD.padding[1];
16601             b = y2 + oDD.padding[2];
16602             l = x1 - oDD.padding[3];
16603
16604             return new Roo.lib.Region( t, r, b, l );
16605         },
16606
16607         /**
16608          * Checks the cursor location to see if it over the target
16609          * @method isOverTarget
16610          * @param {Roo.lib.Point} pt The point to evaluate
16611          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16612          * @return {boolean} true if the mouse is over the target
16613          * @private
16614          * @static
16615          */
16616         isOverTarget: function(pt, oTarget, intersect) {
16617             // use cache if available
16618             var loc = this.locationCache[oTarget.id];
16619             if (!loc || !this.useCache) {
16620                 loc = this.getLocation(oTarget);
16621                 this.locationCache[oTarget.id] = loc;
16622
16623             }
16624
16625             if (!loc) {
16626                 return false;
16627             }
16628
16629             oTarget.cursorIsOver = loc.contains( pt );
16630
16631             // DragDrop is using this as a sanity check for the initial mousedown
16632             // in this case we are done.  In POINT mode, if the drag obj has no
16633             // contraints, we are also done. Otherwise we need to evaluate the
16634             // location of the target as related to the actual location of the
16635             // dragged element.
16636             var dc = this.dragCurrent;
16637             if (!dc || !dc.getTargetCoord ||
16638                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16639                 return oTarget.cursorIsOver;
16640             }
16641
16642             oTarget.overlap = null;
16643
16644             // Get the current location of the drag element, this is the
16645             // location of the mouse event less the delta that represents
16646             // where the original mousedown happened on the element.  We
16647             // need to consider constraints and ticks as well.
16648             var pos = dc.getTargetCoord(pt.x, pt.y);
16649
16650             var el = dc.getDragEl();
16651             var curRegion = new Roo.lib.Region( pos.y,
16652                                                    pos.x + el.offsetWidth,
16653                                                    pos.y + el.offsetHeight,
16654                                                    pos.x );
16655
16656             var overlap = curRegion.intersect(loc);
16657
16658             if (overlap) {
16659                 oTarget.overlap = overlap;
16660                 return (intersect) ? true : oTarget.cursorIsOver;
16661             } else {
16662                 return false;
16663             }
16664         },
16665
16666         /**
16667          * unload event handler
16668          * @method _onUnload
16669          * @private
16670          * @static
16671          */
16672         _onUnload: function(e, me) {
16673             Roo.dd.DragDropMgr.unregAll();
16674         },
16675
16676         /**
16677          * Cleans up the drag and drop events and objects.
16678          * @method unregAll
16679          * @private
16680          * @static
16681          */
16682         unregAll: function() {
16683
16684             if (this.dragCurrent) {
16685                 this.stopDrag();
16686                 this.dragCurrent = null;
16687             }
16688
16689             this._execOnAll("unreg", []);
16690
16691             for (i in this.elementCache) {
16692                 delete this.elementCache[i];
16693             }
16694
16695             this.elementCache = {};
16696             this.ids = {};
16697         },
16698
16699         /**
16700          * A cache of DOM elements
16701          * @property elementCache
16702          * @private
16703          * @static
16704          */
16705         elementCache: {},
16706
16707         /**
16708          * Get the wrapper for the DOM element specified
16709          * @method getElWrapper
16710          * @param {String} id the id of the element to get
16711          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16712          * @private
16713          * @deprecated This wrapper isn't that useful
16714          * @static
16715          */
16716         getElWrapper: function(id) {
16717             var oWrapper = this.elementCache[id];
16718             if (!oWrapper || !oWrapper.el) {
16719                 oWrapper = this.elementCache[id] =
16720                     new this.ElementWrapper(Roo.getDom(id));
16721             }
16722             return oWrapper;
16723         },
16724
16725         /**
16726          * Returns the actual DOM element
16727          * @method getElement
16728          * @param {String} id the id of the elment to get
16729          * @return {Object} The element
16730          * @deprecated use Roo.getDom instead
16731          * @static
16732          */
16733         getElement: function(id) {
16734             return Roo.getDom(id);
16735         },
16736
16737         /**
16738          * Returns the style property for the DOM element (i.e.,
16739          * document.getElById(id).style)
16740          * @method getCss
16741          * @param {String} id the id of the elment to get
16742          * @return {Object} The style property of the element
16743          * @deprecated use Roo.getDom instead
16744          * @static
16745          */
16746         getCss: function(id) {
16747             var el = Roo.getDom(id);
16748             return (el) ? el.style : null;
16749         },
16750
16751         /**
16752          * Inner class for cached elements
16753          * @class DragDropMgr.ElementWrapper
16754          * @for DragDropMgr
16755          * @private
16756          * @deprecated
16757          */
16758         ElementWrapper: function(el) {
16759                 /**
16760                  * The element
16761                  * @property el
16762                  */
16763                 this.el = el || null;
16764                 /**
16765                  * The element id
16766                  * @property id
16767                  */
16768                 this.id = this.el && el.id;
16769                 /**
16770                  * A reference to the style property
16771                  * @property css
16772                  */
16773                 this.css = this.el && el.style;
16774             },
16775
16776         /**
16777          * Returns the X position of an html element
16778          * @method getPosX
16779          * @param el the element for which to get the position
16780          * @return {int} the X coordinate
16781          * @for DragDropMgr
16782          * @deprecated use Roo.lib.Dom.getX instead
16783          * @static
16784          */
16785         getPosX: function(el) {
16786             return Roo.lib.Dom.getX(el);
16787         },
16788
16789         /**
16790          * Returns the Y position of an html element
16791          * @method getPosY
16792          * @param el the element for which to get the position
16793          * @return {int} the Y coordinate
16794          * @deprecated use Roo.lib.Dom.getY instead
16795          * @static
16796          */
16797         getPosY: function(el) {
16798             return Roo.lib.Dom.getY(el);
16799         },
16800
16801         /**
16802          * Swap two nodes.  In IE, we use the native method, for others we
16803          * emulate the IE behavior
16804          * @method swapNode
16805          * @param n1 the first node to swap
16806          * @param n2 the other node to swap
16807          * @static
16808          */
16809         swapNode: function(n1, n2) {
16810             if (n1.swapNode) {
16811                 n1.swapNode(n2);
16812             } else {
16813                 var p = n2.parentNode;
16814                 var s = n2.nextSibling;
16815
16816                 if (s == n1) {
16817                     p.insertBefore(n1, n2);
16818                 } else if (n2 == n1.nextSibling) {
16819                     p.insertBefore(n2, n1);
16820                 } else {
16821                     n1.parentNode.replaceChild(n2, n1);
16822                     p.insertBefore(n1, s);
16823                 }
16824             }
16825         },
16826
16827         /**
16828          * Returns the current scroll position
16829          * @method getScroll
16830          * @private
16831          * @static
16832          */
16833         getScroll: function () {
16834             var t, l, dde=document.documentElement, db=document.body;
16835             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16836                 t = dde.scrollTop;
16837                 l = dde.scrollLeft;
16838             } else if (db) {
16839                 t = db.scrollTop;
16840                 l = db.scrollLeft;
16841             } else {
16842
16843             }
16844             return { top: t, left: l };
16845         },
16846
16847         /**
16848          * Returns the specified element style property
16849          * @method getStyle
16850          * @param {HTMLElement} el          the element
16851          * @param {string}      styleProp   the style property
16852          * @return {string} The value of the style property
16853          * @deprecated use Roo.lib.Dom.getStyle
16854          * @static
16855          */
16856         getStyle: function(el, styleProp) {
16857             return Roo.fly(el).getStyle(styleProp);
16858         },
16859
16860         /**
16861          * Gets the scrollTop
16862          * @method getScrollTop
16863          * @return {int} the document's scrollTop
16864          * @static
16865          */
16866         getScrollTop: function () { return this.getScroll().top; },
16867
16868         /**
16869          * Gets the scrollLeft
16870          * @method getScrollLeft
16871          * @return {int} the document's scrollTop
16872          * @static
16873          */
16874         getScrollLeft: function () { return this.getScroll().left; },
16875
16876         /**
16877          * Sets the x/y position of an element to the location of the
16878          * target element.
16879          * @method moveToEl
16880          * @param {HTMLElement} moveEl      The element to move
16881          * @param {HTMLElement} targetEl    The position reference element
16882          * @static
16883          */
16884         moveToEl: function (moveEl, targetEl) {
16885             var aCoord = Roo.lib.Dom.getXY(targetEl);
16886             Roo.lib.Dom.setXY(moveEl, aCoord);
16887         },
16888
16889         /**
16890          * Numeric array sort function
16891          * @method numericSort
16892          * @static
16893          */
16894         numericSort: function(a, b) { return (a - b); },
16895
16896         /**
16897          * Internal counter
16898          * @property _timeoutCount
16899          * @private
16900          * @static
16901          */
16902         _timeoutCount: 0,
16903
16904         /**
16905          * Trying to make the load order less important.  Without this we get
16906          * an error if this file is loaded before the Event Utility.
16907          * @method _addListeners
16908          * @private
16909          * @static
16910          */
16911         _addListeners: function() {
16912             var DDM = Roo.dd.DDM;
16913             if ( Roo.lib.Event && document ) {
16914                 DDM._onLoad();
16915             } else {
16916                 if (DDM._timeoutCount > 2000) {
16917                 } else {
16918                     setTimeout(DDM._addListeners, 10);
16919                     if (document && document.body) {
16920                         DDM._timeoutCount += 1;
16921                     }
16922                 }
16923             }
16924         },
16925
16926         /**
16927          * Recursively searches the immediate parent and all child nodes for
16928          * the handle element in order to determine wheter or not it was
16929          * clicked.
16930          * @method handleWasClicked
16931          * @param node the html element to inspect
16932          * @static
16933          */
16934         handleWasClicked: function(node, id) {
16935             if (this.isHandle(id, node.id)) {
16936                 return true;
16937             } else {
16938                 // check to see if this is a text node child of the one we want
16939                 var p = node.parentNode;
16940
16941                 while (p) {
16942                     if (this.isHandle(id, p.id)) {
16943                         return true;
16944                     } else {
16945                         p = p.parentNode;
16946                     }
16947                 }
16948             }
16949
16950             return false;
16951         }
16952
16953     };
16954
16955 }();
16956
16957 // shorter alias, save a few bytes
16958 Roo.dd.DDM = Roo.dd.DragDropMgr;
16959 Roo.dd.DDM._addListeners();
16960
16961 }/*
16962  * Based on:
16963  * Ext JS Library 1.1.1
16964  * Copyright(c) 2006-2007, Ext JS, LLC.
16965  *
16966  * Originally Released Under LGPL - original licence link has changed is not relivant.
16967  *
16968  * Fork - LGPL
16969  * <script type="text/javascript">
16970  */
16971
16972 /**
16973  * @class Roo.dd.DD
16974  * A DragDrop implementation where the linked element follows the
16975  * mouse cursor during a drag.
16976  * @extends Roo.dd.DragDrop
16977  * @constructor
16978  * @param {String} id the id of the linked element
16979  * @param {String} sGroup the group of related DragDrop items
16980  * @param {object} config an object containing configurable attributes
16981  *                Valid properties for DD:
16982  *                    scroll
16983  */
16984 Roo.dd.DD = function(id, sGroup, config) {
16985     if (id) {
16986         this.init(id, sGroup, config);
16987     }
16988 };
16989
16990 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16991
16992     /**
16993      * When set to true, the utility automatically tries to scroll the browser
16994      * window wehn a drag and drop element is dragged near the viewport boundary.
16995      * Defaults to true.
16996      * @property scroll
16997      * @type boolean
16998      */
16999     scroll: true,
17000
17001     /**
17002      * Sets the pointer offset to the distance between the linked element's top
17003      * left corner and the location the element was clicked
17004      * @method autoOffset
17005      * @param {int} iPageX the X coordinate of the click
17006      * @param {int} iPageY the Y coordinate of the click
17007      */
17008     autoOffset: function(iPageX, iPageY) {
17009         var x = iPageX - this.startPageX;
17010         var y = iPageY - this.startPageY;
17011         this.setDelta(x, y);
17012     },
17013
17014     /**
17015      * Sets the pointer offset.  You can call this directly to force the
17016      * offset to be in a particular location (e.g., pass in 0,0 to set it
17017      * to the center of the object)
17018      * @method setDelta
17019      * @param {int} iDeltaX the distance from the left
17020      * @param {int} iDeltaY the distance from the top
17021      */
17022     setDelta: function(iDeltaX, iDeltaY) {
17023         this.deltaX = iDeltaX;
17024         this.deltaY = iDeltaY;
17025     },
17026
17027     /**
17028      * Sets the drag element to the location of the mousedown or click event,
17029      * maintaining the cursor location relative to the location on the element
17030      * that was clicked.  Override this if you want to place the element in a
17031      * location other than where the cursor is.
17032      * @method setDragElPos
17033      * @param {int} iPageX the X coordinate of the mousedown or drag event
17034      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17035      */
17036     setDragElPos: function(iPageX, iPageY) {
17037         // the first time we do this, we are going to check to make sure
17038         // the element has css positioning
17039
17040         var el = this.getDragEl();
17041         this.alignElWithMouse(el, iPageX, iPageY);
17042     },
17043
17044     /**
17045      * Sets the element to the location of the mousedown or click event,
17046      * maintaining the cursor location relative to the location on the element
17047      * that was clicked.  Override this if you want to place the element in a
17048      * location other than where the cursor is.
17049      * @method alignElWithMouse
17050      * @param {HTMLElement} el the element to move
17051      * @param {int} iPageX the X coordinate of the mousedown or drag event
17052      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17053      */
17054     alignElWithMouse: function(el, iPageX, iPageY) {
17055         var oCoord = this.getTargetCoord(iPageX, iPageY);
17056         var fly = el.dom ? el : Roo.fly(el);
17057         if (!this.deltaSetXY) {
17058             var aCoord = [oCoord.x, oCoord.y];
17059             fly.setXY(aCoord);
17060             var newLeft = fly.getLeft(true);
17061             var newTop  = fly.getTop(true);
17062             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17063         } else {
17064             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17065         }
17066
17067         this.cachePosition(oCoord.x, oCoord.y);
17068         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17069         return oCoord;
17070     },
17071
17072     /**
17073      * Saves the most recent position so that we can reset the constraints and
17074      * tick marks on-demand.  We need to know this so that we can calculate the
17075      * number of pixels the element is offset from its original position.
17076      * @method cachePosition
17077      * @param iPageX the current x position (optional, this just makes it so we
17078      * don't have to look it up again)
17079      * @param iPageY the current y position (optional, this just makes it so we
17080      * don't have to look it up again)
17081      */
17082     cachePosition: function(iPageX, iPageY) {
17083         if (iPageX) {
17084             this.lastPageX = iPageX;
17085             this.lastPageY = iPageY;
17086         } else {
17087             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17088             this.lastPageX = aCoord[0];
17089             this.lastPageY = aCoord[1];
17090         }
17091     },
17092
17093     /**
17094      * Auto-scroll the window if the dragged object has been moved beyond the
17095      * visible window boundary.
17096      * @method autoScroll
17097      * @param {int} x the drag element's x position
17098      * @param {int} y the drag element's y position
17099      * @param {int} h the height of the drag element
17100      * @param {int} w the width of the drag element
17101      * @private
17102      */
17103     autoScroll: function(x, y, h, w) {
17104
17105         if (this.scroll) {
17106             // The client height
17107             var clientH = Roo.lib.Dom.getViewWidth();
17108
17109             // The client width
17110             var clientW = Roo.lib.Dom.getViewHeight();
17111
17112             // The amt scrolled down
17113             var st = this.DDM.getScrollTop();
17114
17115             // The amt scrolled right
17116             var sl = this.DDM.getScrollLeft();
17117
17118             // Location of the bottom of the element
17119             var bot = h + y;
17120
17121             // Location of the right of the element
17122             var right = w + x;
17123
17124             // The distance from the cursor to the bottom of the visible area,
17125             // adjusted so that we don't scroll if the cursor is beyond the
17126             // element drag constraints
17127             var toBot = (clientH + st - y - this.deltaY);
17128
17129             // The distance from the cursor to the right of the visible area
17130             var toRight = (clientW + sl - x - this.deltaX);
17131
17132
17133             // How close to the edge the cursor must be before we scroll
17134             // var thresh = (document.all) ? 100 : 40;
17135             var thresh = 40;
17136
17137             // How many pixels to scroll per autoscroll op.  This helps to reduce
17138             // clunky scrolling. IE is more sensitive about this ... it needs this
17139             // value to be higher.
17140             var scrAmt = (document.all) ? 80 : 30;
17141
17142             // Scroll down if we are near the bottom of the visible page and the
17143             // obj extends below the crease
17144             if ( bot > clientH && toBot < thresh ) {
17145                 window.scrollTo(sl, st + scrAmt);
17146             }
17147
17148             // Scroll up if the window is scrolled down and the top of the object
17149             // goes above the top border
17150             if ( y < st && st > 0 && y - st < thresh ) {
17151                 window.scrollTo(sl, st - scrAmt);
17152             }
17153
17154             // Scroll right if the obj is beyond the right border and the cursor is
17155             // near the border.
17156             if ( right > clientW && toRight < thresh ) {
17157                 window.scrollTo(sl + scrAmt, st);
17158             }
17159
17160             // Scroll left if the window has been scrolled to the right and the obj
17161             // extends past the left border
17162             if ( x < sl && sl > 0 && x - sl < thresh ) {
17163                 window.scrollTo(sl - scrAmt, st);
17164             }
17165         }
17166     },
17167
17168     /**
17169      * Finds the location the element should be placed if we want to move
17170      * it to where the mouse location less the click offset would place us.
17171      * @method getTargetCoord
17172      * @param {int} iPageX the X coordinate of the click
17173      * @param {int} iPageY the Y coordinate of the click
17174      * @return an object that contains the coordinates (Object.x and Object.y)
17175      * @private
17176      */
17177     getTargetCoord: function(iPageX, iPageY) {
17178
17179
17180         var x = iPageX - this.deltaX;
17181         var y = iPageY - this.deltaY;
17182
17183         if (this.constrainX) {
17184             if (x < this.minX) { x = this.minX; }
17185             if (x > this.maxX) { x = this.maxX; }
17186         }
17187
17188         if (this.constrainY) {
17189             if (y < this.minY) { y = this.minY; }
17190             if (y > this.maxY) { y = this.maxY; }
17191         }
17192
17193         x = this.getTick(x, this.xTicks);
17194         y = this.getTick(y, this.yTicks);
17195
17196
17197         return {x:x, y:y};
17198     },
17199
17200     /*
17201      * Sets up config options specific to this class. Overrides
17202      * Roo.dd.DragDrop, but all versions of this method through the
17203      * inheritance chain are called
17204      */
17205     applyConfig: function() {
17206         Roo.dd.DD.superclass.applyConfig.call(this);
17207         this.scroll = (this.config.scroll !== false);
17208     },
17209
17210     /*
17211      * Event that fires prior to the onMouseDown event.  Overrides
17212      * Roo.dd.DragDrop.
17213      */
17214     b4MouseDown: function(e) {
17215         // this.resetConstraints();
17216         this.autoOffset(e.getPageX(),
17217                             e.getPageY());
17218     },
17219
17220     /*
17221      * Event that fires prior to the onDrag event.  Overrides
17222      * Roo.dd.DragDrop.
17223      */
17224     b4Drag: function(e) {
17225         this.setDragElPos(e.getPageX(),
17226                             e.getPageY());
17227     },
17228
17229     toString: function() {
17230         return ("DD " + this.id);
17231     }
17232
17233     //////////////////////////////////////////////////////////////////////////
17234     // Debugging ygDragDrop events that can be overridden
17235     //////////////////////////////////////////////////////////////////////////
17236     /*
17237     startDrag: function(x, y) {
17238     },
17239
17240     onDrag: function(e) {
17241     },
17242
17243     onDragEnter: function(e, id) {
17244     },
17245
17246     onDragOver: function(e, id) {
17247     },
17248
17249     onDragOut: function(e, id) {
17250     },
17251
17252     onDragDrop: function(e, id) {
17253     },
17254
17255     endDrag: function(e) {
17256     }
17257
17258     */
17259
17260 });/*
17261  * Based on:
17262  * Ext JS Library 1.1.1
17263  * Copyright(c) 2006-2007, Ext JS, LLC.
17264  *
17265  * Originally Released Under LGPL - original licence link has changed is not relivant.
17266  *
17267  * Fork - LGPL
17268  * <script type="text/javascript">
17269  */
17270
17271 /**
17272  * @class Roo.dd.DDProxy
17273  * A DragDrop implementation that inserts an empty, bordered div into
17274  * the document that follows the cursor during drag operations.  At the time of
17275  * the click, the frame div is resized to the dimensions of the linked html
17276  * element, and moved to the exact location of the linked element.
17277  *
17278  * References to the "frame" element refer to the single proxy element that
17279  * was created to be dragged in place of all DDProxy elements on the
17280  * page.
17281  *
17282  * @extends Roo.dd.DD
17283  * @constructor
17284  * @param {String} id the id of the linked html element
17285  * @param {String} sGroup the group of related DragDrop objects
17286  * @param {object} config an object containing configurable attributes
17287  *                Valid properties for DDProxy in addition to those in DragDrop:
17288  *                   resizeFrame, centerFrame, dragElId
17289  */
17290 Roo.dd.DDProxy = function(id, sGroup, config) {
17291     if (id) {
17292         this.init(id, sGroup, config);
17293         this.initFrame();
17294     }
17295 };
17296
17297 /**
17298  * The default drag frame div id
17299  * @property Roo.dd.DDProxy.dragElId
17300  * @type String
17301  * @static
17302  */
17303 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17304
17305 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17306
17307     /**
17308      * By default we resize the drag frame to be the same size as the element
17309      * we want to drag (this is to get the frame effect).  We can turn it off
17310      * if we want a different behavior.
17311      * @property resizeFrame
17312      * @type boolean
17313      */
17314     resizeFrame: true,
17315
17316     /**
17317      * By default the frame is positioned exactly where the drag element is, so
17318      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17319      * you do not have constraints on the obj is to have the drag frame centered
17320      * around the cursor.  Set centerFrame to true for this effect.
17321      * @property centerFrame
17322      * @type boolean
17323      */
17324     centerFrame: false,
17325
17326     /**
17327      * Creates the proxy element if it does not yet exist
17328      * @method createFrame
17329      */
17330     createFrame: function() {
17331         var self = this;
17332         var body = document.body;
17333
17334         if (!body || !body.firstChild) {
17335             setTimeout( function() { self.createFrame(); }, 50 );
17336             return;
17337         }
17338
17339         var div = this.getDragEl();
17340
17341         if (!div) {
17342             div    = document.createElement("div");
17343             div.id = this.dragElId;
17344             var s  = div.style;
17345
17346             s.position   = "absolute";
17347             s.visibility = "hidden";
17348             s.cursor     = "move";
17349             s.border     = "2px solid #aaa";
17350             s.zIndex     = 999;
17351
17352             // appendChild can blow up IE if invoked prior to the window load event
17353             // while rendering a table.  It is possible there are other scenarios
17354             // that would cause this to happen as well.
17355             body.insertBefore(div, body.firstChild);
17356         }
17357     },
17358
17359     /**
17360      * Initialization for the drag frame element.  Must be called in the
17361      * constructor of all subclasses
17362      * @method initFrame
17363      */
17364     initFrame: function() {
17365         this.createFrame();
17366     },
17367
17368     applyConfig: function() {
17369         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17370
17371         this.resizeFrame = (this.config.resizeFrame !== false);
17372         this.centerFrame = (this.config.centerFrame);
17373         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17374     },
17375
17376     /**
17377      * Resizes the drag frame to the dimensions of the clicked object, positions
17378      * it over the object, and finally displays it
17379      * @method showFrame
17380      * @param {int} iPageX X click position
17381      * @param {int} iPageY Y click position
17382      * @private
17383      */
17384     showFrame: function(iPageX, iPageY) {
17385         var el = this.getEl();
17386         var dragEl = this.getDragEl();
17387         var s = dragEl.style;
17388
17389         this._resizeProxy();
17390
17391         if (this.centerFrame) {
17392             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17393                            Math.round(parseInt(s.height, 10)/2) );
17394         }
17395
17396         this.setDragElPos(iPageX, iPageY);
17397
17398         Roo.fly(dragEl).show();
17399     },
17400
17401     /**
17402      * The proxy is automatically resized to the dimensions of the linked
17403      * element when a drag is initiated, unless resizeFrame is set to false
17404      * @method _resizeProxy
17405      * @private
17406      */
17407     _resizeProxy: function() {
17408         if (this.resizeFrame) {
17409             var el = this.getEl();
17410             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17411         }
17412     },
17413
17414     // overrides Roo.dd.DragDrop
17415     b4MouseDown: function(e) {
17416         var x = e.getPageX();
17417         var y = e.getPageY();
17418         this.autoOffset(x, y);
17419         this.setDragElPos(x, y);
17420     },
17421
17422     // overrides Roo.dd.DragDrop
17423     b4StartDrag: function(x, y) {
17424         // show the drag frame
17425         this.showFrame(x, y);
17426     },
17427
17428     // overrides Roo.dd.DragDrop
17429     b4EndDrag: function(e) {
17430         Roo.fly(this.getDragEl()).hide();
17431     },
17432
17433     // overrides Roo.dd.DragDrop
17434     // By default we try to move the element to the last location of the frame.
17435     // This is so that the default behavior mirrors that of Roo.dd.DD.
17436     endDrag: function(e) {
17437
17438         var lel = this.getEl();
17439         var del = this.getDragEl();
17440
17441         // Show the drag frame briefly so we can get its position
17442         del.style.visibility = "";
17443
17444         this.beforeMove();
17445         // Hide the linked element before the move to get around a Safari
17446         // rendering bug.
17447         lel.style.visibility = "hidden";
17448         Roo.dd.DDM.moveToEl(lel, del);
17449         del.style.visibility = "hidden";
17450         lel.style.visibility = "";
17451
17452         this.afterDrag();
17453     },
17454
17455     beforeMove : function(){
17456
17457     },
17458
17459     afterDrag : function(){
17460
17461     },
17462
17463     toString: function() {
17464         return ("DDProxy " + this.id);
17465     }
17466
17467 });
17468 /*
17469  * Based on:
17470  * Ext JS Library 1.1.1
17471  * Copyright(c) 2006-2007, Ext JS, LLC.
17472  *
17473  * Originally Released Under LGPL - original licence link has changed is not relivant.
17474  *
17475  * Fork - LGPL
17476  * <script type="text/javascript">
17477  */
17478
17479  /**
17480  * @class Roo.dd.DDTarget
17481  * A DragDrop implementation that does not move, but can be a drop
17482  * target.  You would get the same result by simply omitting implementation
17483  * for the event callbacks, but this way we reduce the processing cost of the
17484  * event listener and the callbacks.
17485  * @extends Roo.dd.DragDrop
17486  * @constructor
17487  * @param {String} id the id of the element that is a drop target
17488  * @param {String} sGroup the group of related DragDrop objects
17489  * @param {object} config an object containing configurable attributes
17490  *                 Valid properties for DDTarget in addition to those in
17491  *                 DragDrop:
17492  *                    none
17493  */
17494 Roo.dd.DDTarget = function(id, sGroup, config) {
17495     if (id) {
17496         this.initTarget(id, sGroup, config);
17497     }
17498     if (config.listeners || config.events) { 
17499        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17500             listeners : config.listeners || {}, 
17501             events : config.events || {} 
17502         });    
17503     }
17504 };
17505
17506 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17507 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17508     toString: function() {
17509         return ("DDTarget " + this.id);
17510     }
17511 });
17512 /*
17513  * Based on:
17514  * Ext JS Library 1.1.1
17515  * Copyright(c) 2006-2007, Ext JS, LLC.
17516  *
17517  * Originally Released Under LGPL - original licence link has changed is not relivant.
17518  *
17519  * Fork - LGPL
17520  * <script type="text/javascript">
17521  */
17522  
17523
17524 /**
17525  * @class Roo.dd.ScrollManager
17526  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17527  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17528  * @singleton
17529  */
17530 Roo.dd.ScrollManager = function(){
17531     var ddm = Roo.dd.DragDropMgr;
17532     var els = {};
17533     var dragEl = null;
17534     var proc = {};
17535     
17536     var onStop = function(e){
17537         dragEl = null;
17538         clearProc();
17539     };
17540     
17541     var triggerRefresh = function(){
17542         if(ddm.dragCurrent){
17543              ddm.refreshCache(ddm.dragCurrent.groups);
17544         }
17545     };
17546     
17547     var doScroll = function(){
17548         if(ddm.dragCurrent){
17549             var dds = Roo.dd.ScrollManager;
17550             if(!dds.animate){
17551                 if(proc.el.scroll(proc.dir, dds.increment)){
17552                     triggerRefresh();
17553                 }
17554             }else{
17555                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17556             }
17557         }
17558     };
17559     
17560     var clearProc = function(){
17561         if(proc.id){
17562             clearInterval(proc.id);
17563         }
17564         proc.id = 0;
17565         proc.el = null;
17566         proc.dir = "";
17567     };
17568     
17569     var startProc = function(el, dir){
17570         clearProc();
17571         proc.el = el;
17572         proc.dir = dir;
17573         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17574     };
17575     
17576     var onFire = function(e, isDrop){
17577         if(isDrop || !ddm.dragCurrent){ return; }
17578         var dds = Roo.dd.ScrollManager;
17579         if(!dragEl || dragEl != ddm.dragCurrent){
17580             dragEl = ddm.dragCurrent;
17581             // refresh regions on drag start
17582             dds.refreshCache();
17583         }
17584         
17585         var xy = Roo.lib.Event.getXY(e);
17586         var pt = new Roo.lib.Point(xy[0], xy[1]);
17587         for(var id in els){
17588             var el = els[id], r = el._region;
17589             if(r && r.contains(pt) && el.isScrollable()){
17590                 if(r.bottom - pt.y <= dds.thresh){
17591                     if(proc.el != el){
17592                         startProc(el, "down");
17593                     }
17594                     return;
17595                 }else if(r.right - pt.x <= dds.thresh){
17596                     if(proc.el != el){
17597                         startProc(el, "left");
17598                     }
17599                     return;
17600                 }else if(pt.y - r.top <= dds.thresh){
17601                     if(proc.el != el){
17602                         startProc(el, "up");
17603                     }
17604                     return;
17605                 }else if(pt.x - r.left <= dds.thresh){
17606                     if(proc.el != el){
17607                         startProc(el, "right");
17608                     }
17609                     return;
17610                 }
17611             }
17612         }
17613         clearProc();
17614     };
17615     
17616     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17617     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17618     
17619     return {
17620         /**
17621          * Registers new overflow element(s) to auto scroll
17622          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17623          */
17624         register : function(el){
17625             if(el instanceof Array){
17626                 for(var i = 0, len = el.length; i < len; i++) {
17627                         this.register(el[i]);
17628                 }
17629             }else{
17630                 el = Roo.get(el);
17631                 els[el.id] = el;
17632             }
17633         },
17634         
17635         /**
17636          * Unregisters overflow element(s) so they are no longer scrolled
17637          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17638          */
17639         unregister : function(el){
17640             if(el instanceof Array){
17641                 for(var i = 0, len = el.length; i < len; i++) {
17642                         this.unregister(el[i]);
17643                 }
17644             }else{
17645                 el = Roo.get(el);
17646                 delete els[el.id];
17647             }
17648         },
17649         
17650         /**
17651          * The number of pixels from the edge of a container the pointer needs to be to 
17652          * trigger scrolling (defaults to 25)
17653          * @type Number
17654          */
17655         thresh : 25,
17656         
17657         /**
17658          * The number of pixels to scroll in each scroll increment (defaults to 50)
17659          * @type Number
17660          */
17661         increment : 100,
17662         
17663         /**
17664          * The frequency of scrolls in milliseconds (defaults to 500)
17665          * @type Number
17666          */
17667         frequency : 500,
17668         
17669         /**
17670          * True to animate the scroll (defaults to true)
17671          * @type Boolean
17672          */
17673         animate: true,
17674         
17675         /**
17676          * The animation duration in seconds - 
17677          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17678          * @type Number
17679          */
17680         animDuration: .4,
17681         
17682         /**
17683          * Manually trigger a cache refresh.
17684          */
17685         refreshCache : function(){
17686             for(var id in els){
17687                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17688                     els[id]._region = els[id].getRegion();
17689                 }
17690             }
17691         }
17692     };
17693 }();/*
17694  * Based on:
17695  * Ext JS Library 1.1.1
17696  * Copyright(c) 2006-2007, Ext JS, LLC.
17697  *
17698  * Originally Released Under LGPL - original licence link has changed is not relivant.
17699  *
17700  * Fork - LGPL
17701  * <script type="text/javascript">
17702  */
17703  
17704
17705 /**
17706  * @class Roo.dd.Registry
17707  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17708  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17709  * @singleton
17710  */
17711 Roo.dd.Registry = function(){
17712     var elements = {}; 
17713     var handles = {}; 
17714     var autoIdSeed = 0;
17715
17716     var getId = function(el, autogen){
17717         if(typeof el == "string"){
17718             return el;
17719         }
17720         var id = el.id;
17721         if(!id && autogen !== false){
17722             id = "roodd-" + (++autoIdSeed);
17723             el.id = id;
17724         }
17725         return id;
17726     };
17727     
17728     return {
17729     /**
17730      * Register a drag drop element
17731      * @param {String|HTMLElement} element The id or DOM node to register
17732      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17733      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17734      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17735      * populated in the data object (if applicable):
17736      * <pre>
17737 Value      Description<br />
17738 ---------  ------------------------------------------<br />
17739 handles    Array of DOM nodes that trigger dragging<br />
17740            for the element being registered<br />
17741 isHandle   True if the element passed in triggers<br />
17742            dragging itself, else false
17743 </pre>
17744      */
17745         register : function(el, data){
17746             data = data || {};
17747             if(typeof el == "string"){
17748                 el = document.getElementById(el);
17749             }
17750             data.ddel = el;
17751             elements[getId(el)] = data;
17752             if(data.isHandle !== false){
17753                 handles[data.ddel.id] = data;
17754             }
17755             if(data.handles){
17756                 var hs = data.handles;
17757                 for(var i = 0, len = hs.length; i < len; i++){
17758                         handles[getId(hs[i])] = data;
17759                 }
17760             }
17761         },
17762
17763     /**
17764      * Unregister a drag drop element
17765      * @param {String|HTMLElement}  element The id or DOM node to unregister
17766      */
17767         unregister : function(el){
17768             var id = getId(el, false);
17769             var data = elements[id];
17770             if(data){
17771                 delete elements[id];
17772                 if(data.handles){
17773                     var hs = data.handles;
17774                     for(var i = 0, len = hs.length; i < len; i++){
17775                         delete handles[getId(hs[i], false)];
17776                     }
17777                 }
17778             }
17779         },
17780
17781     /**
17782      * Returns the handle registered for a DOM Node by id
17783      * @param {String|HTMLElement} id The DOM node or id to look up
17784      * @return {Object} handle The custom handle data
17785      */
17786         getHandle : function(id){
17787             if(typeof id != "string"){ // must be element?
17788                 id = id.id;
17789             }
17790             return handles[id];
17791         },
17792
17793     /**
17794      * Returns the handle that is registered for the DOM node that is the target of the event
17795      * @param {Event} e The event
17796      * @return {Object} handle The custom handle data
17797      */
17798         getHandleFromEvent : function(e){
17799             var t = Roo.lib.Event.getTarget(e);
17800             return t ? handles[t.id] : null;
17801         },
17802
17803     /**
17804      * Returns a custom data object that is registered for a DOM node by id
17805      * @param {String|HTMLElement} id The DOM node or id to look up
17806      * @return {Object} data The custom data
17807      */
17808         getTarget : function(id){
17809             if(typeof id != "string"){ // must be element?
17810                 id = id.id;
17811             }
17812             return elements[id];
17813         },
17814
17815     /**
17816      * Returns a custom data object that is registered for the DOM node that is the target of the event
17817      * @param {Event} e The event
17818      * @return {Object} data The custom data
17819      */
17820         getTargetFromEvent : function(e){
17821             var t = Roo.lib.Event.getTarget(e);
17822             return t ? elements[t.id] || handles[t.id] : null;
17823         }
17824     };
17825 }();/*
17826  * Based on:
17827  * Ext JS Library 1.1.1
17828  * Copyright(c) 2006-2007, Ext JS, LLC.
17829  *
17830  * Originally Released Under LGPL - original licence link has changed is not relivant.
17831  *
17832  * Fork - LGPL
17833  * <script type="text/javascript">
17834  */
17835  
17836
17837 /**
17838  * @class Roo.dd.StatusProxy
17839  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17840  * default drag proxy used by all Roo.dd components.
17841  * @constructor
17842  * @param {Object} config
17843  */
17844 Roo.dd.StatusProxy = function(config){
17845     Roo.apply(this, config);
17846     this.id = this.id || Roo.id();
17847     this.el = new Roo.Layer({
17848         dh: {
17849             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17850                 {tag: "div", cls: "x-dd-drop-icon"},
17851                 {tag: "div", cls: "x-dd-drag-ghost"}
17852             ]
17853         }, 
17854         shadow: !config || config.shadow !== false
17855     });
17856     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17857     this.dropStatus = this.dropNotAllowed;
17858 };
17859
17860 Roo.dd.StatusProxy.prototype = {
17861     /**
17862      * @cfg {String} dropAllowed
17863      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17864      */
17865     dropAllowed : "x-dd-drop-ok",
17866     /**
17867      * @cfg {String} dropNotAllowed
17868      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17869      */
17870     dropNotAllowed : "x-dd-drop-nodrop",
17871
17872     /**
17873      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17874      * over the current target element.
17875      * @param {String} cssClass The css class for the new drop status indicator image
17876      */
17877     setStatus : function(cssClass){
17878         cssClass = cssClass || this.dropNotAllowed;
17879         if(this.dropStatus != cssClass){
17880             this.el.replaceClass(this.dropStatus, cssClass);
17881             this.dropStatus = cssClass;
17882         }
17883     },
17884
17885     /**
17886      * Resets the status indicator to the default dropNotAllowed value
17887      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17888      */
17889     reset : function(clearGhost){
17890         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17891         this.dropStatus = this.dropNotAllowed;
17892         if(clearGhost){
17893             this.ghost.update("");
17894         }
17895     },
17896
17897     /**
17898      * Updates the contents of the ghost element
17899      * @param {String} html The html that will replace the current innerHTML of the ghost element
17900      */
17901     update : function(html){
17902         if(typeof html == "string"){
17903             this.ghost.update(html);
17904         }else{
17905             this.ghost.update("");
17906             html.style.margin = "0";
17907             this.ghost.dom.appendChild(html);
17908         }
17909         // ensure float = none set?? cant remember why though.
17910         var el = this.ghost.dom.firstChild;
17911                 if(el){
17912                         Roo.fly(el).setStyle('float', 'none');
17913                 }
17914     },
17915     
17916     /**
17917      * Returns the underlying proxy {@link Roo.Layer}
17918      * @return {Roo.Layer} el
17919     */
17920     getEl : function(){
17921         return this.el;
17922     },
17923
17924     /**
17925      * Returns the ghost element
17926      * @return {Roo.Element} el
17927      */
17928     getGhost : function(){
17929         return this.ghost;
17930     },
17931
17932     /**
17933      * Hides the proxy
17934      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17935      */
17936     hide : function(clear){
17937         this.el.hide();
17938         if(clear){
17939             this.reset(true);
17940         }
17941     },
17942
17943     /**
17944      * Stops the repair animation if it's currently running
17945      */
17946     stop : function(){
17947         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17948             this.anim.stop();
17949         }
17950     },
17951
17952     /**
17953      * Displays this proxy
17954      */
17955     show : function(){
17956         this.el.show();
17957     },
17958
17959     /**
17960      * Force the Layer to sync its shadow and shim positions to the element
17961      */
17962     sync : function(){
17963         this.el.sync();
17964     },
17965
17966     /**
17967      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17968      * invalid drop operation by the item being dragged.
17969      * @param {Array} xy The XY position of the element ([x, y])
17970      * @param {Function} callback The function to call after the repair is complete
17971      * @param {Object} scope The scope in which to execute the callback
17972      */
17973     repair : function(xy, callback, scope){
17974         this.callback = callback;
17975         this.scope = scope;
17976         if(xy && this.animRepair !== false){
17977             this.el.addClass("x-dd-drag-repair");
17978             this.el.hideUnders(true);
17979             this.anim = this.el.shift({
17980                 duration: this.repairDuration || .5,
17981                 easing: 'easeOut',
17982                 xy: xy,
17983                 stopFx: true,
17984                 callback: this.afterRepair,
17985                 scope: this
17986             });
17987         }else{
17988             this.afterRepair();
17989         }
17990     },
17991
17992     // private
17993     afterRepair : function(){
17994         this.hide(true);
17995         if(typeof this.callback == "function"){
17996             this.callback.call(this.scope || this);
17997         }
17998         this.callback = null;
17999         this.scope = null;
18000     }
18001 };/*
18002  * Based on:
18003  * Ext JS Library 1.1.1
18004  * Copyright(c) 2006-2007, Ext JS, LLC.
18005  *
18006  * Originally Released Under LGPL - original licence link has changed is not relivant.
18007  *
18008  * Fork - LGPL
18009  * <script type="text/javascript">
18010  */
18011
18012 /**
18013  * @class Roo.dd.DragSource
18014  * @extends Roo.dd.DDProxy
18015  * A simple class that provides the basic implementation needed to make any element draggable.
18016  * @constructor
18017  * @param {String/HTMLElement/Element} el The container element
18018  * @param {Object} config
18019  */
18020 Roo.dd.DragSource = function(el, config){
18021     this.el = Roo.get(el);
18022     this.dragData = {};
18023     
18024     Roo.apply(this, config);
18025     
18026     if(!this.proxy){
18027         this.proxy = new Roo.dd.StatusProxy();
18028     }
18029
18030     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18031           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18032     
18033     this.dragging = false;
18034 };
18035
18036 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18037     /**
18038      * @cfg {String} dropAllowed
18039      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18040      */
18041     dropAllowed : "x-dd-drop-ok",
18042     /**
18043      * @cfg {String} dropNotAllowed
18044      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18045      */
18046     dropNotAllowed : "x-dd-drop-nodrop",
18047
18048     /**
18049      * Returns the data object associated with this drag source
18050      * @return {Object} data An object containing arbitrary data
18051      */
18052     getDragData : function(e){
18053         return this.dragData;
18054     },
18055
18056     // private
18057     onDragEnter : function(e, id){
18058         var target = Roo.dd.DragDropMgr.getDDById(id);
18059         this.cachedTarget = target;
18060         if(this.beforeDragEnter(target, e, id) !== false){
18061             if(target.isNotifyTarget){
18062                 var status = target.notifyEnter(this, e, this.dragData);
18063                 this.proxy.setStatus(status);
18064             }else{
18065                 this.proxy.setStatus(this.dropAllowed);
18066             }
18067             
18068             if(this.afterDragEnter){
18069                 /**
18070                  * An empty function by default, but provided so that you can perform a custom action
18071                  * when the dragged item enters the drop target by providing an implementation.
18072                  * @param {Roo.dd.DragDrop} target The drop target
18073                  * @param {Event} e The event object
18074                  * @param {String} id The id of the dragged element
18075                  * @method afterDragEnter
18076                  */
18077                 this.afterDragEnter(target, e, id);
18078             }
18079         }
18080     },
18081
18082     /**
18083      * An empty function by default, but provided so that you can perform a custom action
18084      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18085      * @param {Roo.dd.DragDrop} target The drop target
18086      * @param {Event} e The event object
18087      * @param {String} id The id of the dragged element
18088      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18089      */
18090     beforeDragEnter : function(target, e, id){
18091         return true;
18092     },
18093
18094     // private
18095     alignElWithMouse: function() {
18096         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18097         this.proxy.sync();
18098     },
18099
18100     // private
18101     onDragOver : function(e, id){
18102         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18103         if(this.beforeDragOver(target, e, id) !== false){
18104             if(target.isNotifyTarget){
18105                 var status = target.notifyOver(this, e, this.dragData);
18106                 this.proxy.setStatus(status);
18107             }
18108
18109             if(this.afterDragOver){
18110                 /**
18111                  * An empty function by default, but provided so that you can perform a custom action
18112                  * while the dragged item is over the drop target by providing an implementation.
18113                  * @param {Roo.dd.DragDrop} target The drop target
18114                  * @param {Event} e The event object
18115                  * @param {String} id The id of the dragged element
18116                  * @method afterDragOver
18117                  */
18118                 this.afterDragOver(target, e, id);
18119             }
18120         }
18121     },
18122
18123     /**
18124      * An empty function by default, but provided so that you can perform a custom action
18125      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18126      * @param {Roo.dd.DragDrop} target The drop target
18127      * @param {Event} e The event object
18128      * @param {String} id The id of the dragged element
18129      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18130      */
18131     beforeDragOver : function(target, e, id){
18132         return true;
18133     },
18134
18135     // private
18136     onDragOut : function(e, id){
18137         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18138         if(this.beforeDragOut(target, e, id) !== false){
18139             if(target.isNotifyTarget){
18140                 target.notifyOut(this, e, this.dragData);
18141             }
18142             this.proxy.reset();
18143             if(this.afterDragOut){
18144                 /**
18145                  * An empty function by default, but provided so that you can perform a custom action
18146                  * after the dragged item is dragged out of the target without dropping.
18147                  * @param {Roo.dd.DragDrop} target The drop target
18148                  * @param {Event} e The event object
18149                  * @param {String} id The id of the dragged element
18150                  * @method afterDragOut
18151                  */
18152                 this.afterDragOut(target, e, id);
18153             }
18154         }
18155         this.cachedTarget = null;
18156     },
18157
18158     /**
18159      * An empty function by default, but provided so that you can perform a custom action before the dragged
18160      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18161      * @param {Roo.dd.DragDrop} target The drop target
18162      * @param {Event} e The event object
18163      * @param {String} id The id of the dragged element
18164      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18165      */
18166     beforeDragOut : function(target, e, id){
18167         return true;
18168     },
18169     
18170     // private
18171     onDragDrop : function(e, id){
18172         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18173         if(this.beforeDragDrop(target, e, id) !== false){
18174             if(target.isNotifyTarget){
18175                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18176                     this.onValidDrop(target, e, id);
18177                 }else{
18178                     this.onInvalidDrop(target, e, id);
18179                 }
18180             }else{
18181                 this.onValidDrop(target, e, id);
18182             }
18183             
18184             if(this.afterDragDrop){
18185                 /**
18186                  * An empty function by default, but provided so that you can perform a custom action
18187                  * after a valid drag drop has occurred by providing an implementation.
18188                  * @param {Roo.dd.DragDrop} target The drop target
18189                  * @param {Event} e The event object
18190                  * @param {String} id The id of the dropped element
18191                  * @method afterDragDrop
18192                  */
18193                 this.afterDragDrop(target, e, id);
18194             }
18195         }
18196         delete this.cachedTarget;
18197     },
18198
18199     /**
18200      * An empty function by default, but provided so that you can perform a custom action before the dragged
18201      * item is dropped onto the target and optionally cancel the onDragDrop.
18202      * @param {Roo.dd.DragDrop} target The drop target
18203      * @param {Event} e The event object
18204      * @param {String} id The id of the dragged element
18205      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18206      */
18207     beforeDragDrop : function(target, e, id){
18208         return true;
18209     },
18210
18211     // private
18212     onValidDrop : function(target, e, id){
18213         this.hideProxy();
18214         if(this.afterValidDrop){
18215             /**
18216              * An empty function by default, but provided so that you can perform a custom action
18217              * after a valid drop has occurred by providing an implementation.
18218              * @param {Object} target The target DD 
18219              * @param {Event} e The event object
18220              * @param {String} id The id of the dropped element
18221              * @method afterInvalidDrop
18222              */
18223             this.afterValidDrop(target, e, id);
18224         }
18225     },
18226
18227     // private
18228     getRepairXY : function(e, data){
18229         return this.el.getXY();  
18230     },
18231
18232     // private
18233     onInvalidDrop : function(target, e, id){
18234         this.beforeInvalidDrop(target, e, id);
18235         if(this.cachedTarget){
18236             if(this.cachedTarget.isNotifyTarget){
18237                 this.cachedTarget.notifyOut(this, e, this.dragData);
18238             }
18239             this.cacheTarget = null;
18240         }
18241         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18242
18243         if(this.afterInvalidDrop){
18244             /**
18245              * An empty function by default, but provided so that you can perform a custom action
18246              * after an invalid drop has occurred by providing an implementation.
18247              * @param {Event} e The event object
18248              * @param {String} id The id of the dropped element
18249              * @method afterInvalidDrop
18250              */
18251             this.afterInvalidDrop(e, id);
18252         }
18253     },
18254
18255     // private
18256     afterRepair : function(){
18257         if(Roo.enableFx){
18258             this.el.highlight(this.hlColor || "c3daf9");
18259         }
18260         this.dragging = false;
18261     },
18262
18263     /**
18264      * An empty function by default, but provided so that you can perform a custom action after an invalid
18265      * drop has occurred.
18266      * @param {Roo.dd.DragDrop} target The drop target
18267      * @param {Event} e The event object
18268      * @param {String} id The id of the dragged element
18269      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18270      */
18271     beforeInvalidDrop : function(target, e, id){
18272         return true;
18273     },
18274
18275     // private
18276     handleMouseDown : function(e){
18277         if(this.dragging) {
18278             return;
18279         }
18280         var data = this.getDragData(e);
18281         if(data && this.onBeforeDrag(data, e) !== false){
18282             this.dragData = data;
18283             this.proxy.stop();
18284             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18285         } 
18286     },
18287
18288     /**
18289      * An empty function by default, but provided so that you can perform a custom action before the initial
18290      * drag event begins and optionally cancel it.
18291      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18292      * @param {Event} e The event object
18293      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18294      */
18295     onBeforeDrag : function(data, e){
18296         return true;
18297     },
18298
18299     /**
18300      * An empty function by default, but provided so that you can perform a custom action once the initial
18301      * drag event has begun.  The drag cannot be canceled from this function.
18302      * @param {Number} x The x position of the click on the dragged object
18303      * @param {Number} y The y position of the click on the dragged object
18304      */
18305     onStartDrag : Roo.emptyFn,
18306
18307     // private - YUI override
18308     startDrag : function(x, y){
18309         this.proxy.reset();
18310         this.dragging = true;
18311         this.proxy.update("");
18312         this.onInitDrag(x, y);
18313         this.proxy.show();
18314     },
18315
18316     // private
18317     onInitDrag : function(x, y){
18318         var clone = this.el.dom.cloneNode(true);
18319         clone.id = Roo.id(); // prevent duplicate ids
18320         this.proxy.update(clone);
18321         this.onStartDrag(x, y);
18322         return true;
18323     },
18324
18325     /**
18326      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18327      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18328      */
18329     getProxy : function(){
18330         return this.proxy;  
18331     },
18332
18333     /**
18334      * Hides the drag source's {@link Roo.dd.StatusProxy}
18335      */
18336     hideProxy : function(){
18337         this.proxy.hide();  
18338         this.proxy.reset(true);
18339         this.dragging = false;
18340     },
18341
18342     // private
18343     triggerCacheRefresh : function(){
18344         Roo.dd.DDM.refreshCache(this.groups);
18345     },
18346
18347     // private - override to prevent hiding
18348     b4EndDrag: function(e) {
18349     },
18350
18351     // private - override to prevent moving
18352     endDrag : function(e){
18353         this.onEndDrag(this.dragData, e);
18354     },
18355
18356     // private
18357     onEndDrag : function(data, e){
18358     },
18359     
18360     // private - pin to cursor
18361     autoOffset : function(x, y) {
18362         this.setDelta(-12, -20);
18363     }    
18364 });/*
18365  * Based on:
18366  * Ext JS Library 1.1.1
18367  * Copyright(c) 2006-2007, Ext JS, LLC.
18368  *
18369  * Originally Released Under LGPL - original licence link has changed is not relivant.
18370  *
18371  * Fork - LGPL
18372  * <script type="text/javascript">
18373  */
18374
18375
18376 /**
18377  * @class Roo.dd.DropTarget
18378  * @extends Roo.dd.DDTarget
18379  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18380  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18381  * @constructor
18382  * @param {String/HTMLElement/Element} el The container element
18383  * @param {Object} config
18384  */
18385 Roo.dd.DropTarget = function(el, config){
18386     this.el = Roo.get(el);
18387     
18388     var listeners = false; ;
18389     if (config && config.listeners) {
18390         listeners= config.listeners;
18391         delete config.listeners;
18392     }
18393     Roo.apply(this, config);
18394     
18395     if(this.containerScroll){
18396         Roo.dd.ScrollManager.register(this.el);
18397     }
18398     this.addEvents( {
18399          /**
18400          * @scope Roo.dd.DropTarget
18401          */
18402          
18403          /**
18404          * @event enter
18405          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18406          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18407          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18408          * 
18409          * IMPORTANT : it should set this.overClass and this.dropAllowed
18410          * 
18411          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18412          * @param {Event} e The event
18413          * @param {Object} data An object containing arbitrary data supplied by the drag source
18414          */
18415         "enter" : true,
18416         
18417          /**
18418          * @event over
18419          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18420          * This method will be called on every mouse movement while the drag source is over the drop target.
18421          * This default implementation simply returns the dropAllowed config value.
18422          * 
18423          * IMPORTANT : it should set this.dropAllowed
18424          * 
18425          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18426          * @param {Event} e The event
18427          * @param {Object} data An object containing arbitrary data supplied by the drag source
18428          
18429          */
18430         "over" : true,
18431         /**
18432          * @event out
18433          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18434          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18435          * overClass (if any) from the drop element.
18436          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18437          * @param {Event} e The event
18438          * @param {Object} data An object containing arbitrary data supplied by the drag source
18439          */
18440          "out" : true,
18441          
18442         /**
18443          * @event drop
18444          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18445          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18446          * implementation that does something to process the drop event and returns true so that the drag source's
18447          * repair action does not run.
18448          * 
18449          * IMPORTANT : it should set this.success
18450          * 
18451          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18452          * @param {Event} e The event
18453          * @param {Object} data An object containing arbitrary data supplied by the drag source
18454         */
18455          "drop" : true
18456     });
18457             
18458      
18459     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18460         this.el.dom, 
18461         this.ddGroup || this.group,
18462         {
18463             isTarget: true,
18464             listeners : listeners || {} 
18465            
18466         
18467         }
18468     );
18469
18470 };
18471
18472 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18473     /**
18474      * @cfg {String} overClass
18475      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18476      */
18477      /**
18478      * @cfg {String} ddGroup
18479      * The drag drop group to handle drop events for
18480      */
18481      
18482     /**
18483      * @cfg {String} dropAllowed
18484      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18485      */
18486     dropAllowed : "x-dd-drop-ok",
18487     /**
18488      * @cfg {String} dropNotAllowed
18489      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18490      */
18491     dropNotAllowed : "x-dd-drop-nodrop",
18492     /**
18493      * @cfg {boolean} success
18494      * set this after drop listener.. 
18495      */
18496     success : false,
18497     /**
18498      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18499      * if the drop point is valid for over/enter..
18500      */
18501     valid : false,
18502     // private
18503     isTarget : true,
18504
18505     // private
18506     isNotifyTarget : true,
18507     
18508     /**
18509      * @hide
18510      */
18511     notifyEnter : function(dd, e, data)
18512     {
18513         this.valid = true;
18514         this.fireEvent('enter', dd, e, data);
18515         if(this.overClass){
18516             this.el.addClass(this.overClass);
18517         }
18518         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18519             this.valid ? this.dropAllowed : this.dropNotAllowed
18520         );
18521     },
18522
18523     /**
18524      * @hide
18525      */
18526     notifyOver : function(dd, e, data)
18527     {
18528         this.valid = true;
18529         this.fireEvent('over', dd, e, data);
18530         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18531             this.valid ? this.dropAllowed : this.dropNotAllowed
18532         );
18533     },
18534
18535     /**
18536      * @hide
18537      */
18538     notifyOut : function(dd, e, data)
18539     {
18540         this.fireEvent('out', dd, e, data);
18541         if(this.overClass){
18542             this.el.removeClass(this.overClass);
18543         }
18544     },
18545
18546     /**
18547      * @hide
18548      */
18549     notifyDrop : function(dd, e, data)
18550     {
18551         this.success = false;
18552         this.fireEvent('drop', dd, e, data);
18553         return this.success;
18554     }
18555 });/*
18556  * Based on:
18557  * Ext JS Library 1.1.1
18558  * Copyright(c) 2006-2007, Ext JS, LLC.
18559  *
18560  * Originally Released Under LGPL - original licence link has changed is not relivant.
18561  *
18562  * Fork - LGPL
18563  * <script type="text/javascript">
18564  */
18565
18566
18567 /**
18568  * @class Roo.dd.DragZone
18569  * @extends Roo.dd.DragSource
18570  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18571  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18572  * @constructor
18573  * @param {String/HTMLElement/Element} el The container element
18574  * @param {Object} config
18575  */
18576 Roo.dd.DragZone = function(el, config){
18577     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18578     if(this.containerScroll){
18579         Roo.dd.ScrollManager.register(this.el);
18580     }
18581 };
18582
18583 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18584     /**
18585      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18586      * for auto scrolling during drag operations.
18587      */
18588     /**
18589      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18590      * method after a failed drop (defaults to "c3daf9" - light blue)
18591      */
18592
18593     /**
18594      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18595      * for a valid target to drag based on the mouse down. Override this method
18596      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18597      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18598      * @param {EventObject} e The mouse down event
18599      * @return {Object} The dragData
18600      */
18601     getDragData : function(e){
18602         return Roo.dd.Registry.getHandleFromEvent(e);
18603     },
18604     
18605     /**
18606      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18607      * this.dragData.ddel
18608      * @param {Number} x The x position of the click on the dragged object
18609      * @param {Number} y The y position of the click on the dragged object
18610      * @return {Boolean} true to continue the drag, false to cancel
18611      */
18612     onInitDrag : function(x, y){
18613         this.proxy.update(this.dragData.ddel.cloneNode(true));
18614         this.onStartDrag(x, y);
18615         return true;
18616     },
18617     
18618     /**
18619      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18620      */
18621     afterRepair : function(){
18622         if(Roo.enableFx){
18623             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18624         }
18625         this.dragging = false;
18626     },
18627
18628     /**
18629      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18630      * the XY of this.dragData.ddel
18631      * @param {EventObject} e The mouse up event
18632      * @return {Array} The xy location (e.g. [100, 200])
18633      */
18634     getRepairXY : function(e){
18635         return Roo.Element.fly(this.dragData.ddel).getXY();  
18636     }
18637 });/*
18638  * Based on:
18639  * Ext JS Library 1.1.1
18640  * Copyright(c) 2006-2007, Ext JS, LLC.
18641  *
18642  * Originally Released Under LGPL - original licence link has changed is not relivant.
18643  *
18644  * Fork - LGPL
18645  * <script type="text/javascript">
18646  */
18647 /**
18648  * @class Roo.dd.DropZone
18649  * @extends Roo.dd.DropTarget
18650  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18651  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18652  * @constructor
18653  * @param {String/HTMLElement/Element} el The container element
18654  * @param {Object} config
18655  */
18656 Roo.dd.DropZone = function(el, config){
18657     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18658 };
18659
18660 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18661     /**
18662      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18663      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18664      * provide your own custom lookup.
18665      * @param {Event} e The event
18666      * @return {Object} data The custom data
18667      */
18668     getTargetFromEvent : function(e){
18669         return Roo.dd.Registry.getTargetFromEvent(e);
18670     },
18671
18672     /**
18673      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18674      * that it has registered.  This method has no default implementation and should be overridden to provide
18675      * node-specific processing if necessary.
18676      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18677      * {@link #getTargetFromEvent} for this node)
18678      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18679      * @param {Event} e The event
18680      * @param {Object} data An object containing arbitrary data supplied by the drag source
18681      */
18682     onNodeEnter : function(n, dd, e, data){
18683         
18684     },
18685
18686     /**
18687      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18688      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18689      * overridden to provide the proper feedback.
18690      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18691      * {@link #getTargetFromEvent} for this node)
18692      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18693      * @param {Event} e The event
18694      * @param {Object} data An object containing arbitrary data supplied by the drag source
18695      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18696      * underlying {@link Roo.dd.StatusProxy} can be updated
18697      */
18698     onNodeOver : function(n, dd, e, data){
18699         return this.dropAllowed;
18700     },
18701
18702     /**
18703      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18704      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18705      * node-specific processing if necessary.
18706      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18707      * {@link #getTargetFromEvent} for this node)
18708      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18709      * @param {Event} e The event
18710      * @param {Object} data An object containing arbitrary data supplied by the drag source
18711      */
18712     onNodeOut : function(n, dd, e, data){
18713         
18714     },
18715
18716     /**
18717      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18718      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18719      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18720      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18721      * {@link #getTargetFromEvent} for this node)
18722      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18723      * @param {Event} e The event
18724      * @param {Object} data An object containing arbitrary data supplied by the drag source
18725      * @return {Boolean} True if the drop was valid, else false
18726      */
18727     onNodeDrop : function(n, dd, e, data){
18728         return false;
18729     },
18730
18731     /**
18732      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18733      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18734      * it should be overridden to provide the proper feedback if necessary.
18735      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18736      * @param {Event} e The event
18737      * @param {Object} data An object containing arbitrary data supplied by the drag source
18738      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18739      * underlying {@link Roo.dd.StatusProxy} can be updated
18740      */
18741     onContainerOver : function(dd, e, data){
18742         return this.dropNotAllowed;
18743     },
18744
18745     /**
18746      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18747      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18748      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18749      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18750      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18751      * @param {Event} e The event
18752      * @param {Object} data An object containing arbitrary data supplied by the drag source
18753      * @return {Boolean} True if the drop was valid, else false
18754      */
18755     onContainerDrop : function(dd, e, data){
18756         return false;
18757     },
18758
18759     /**
18760      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18761      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18762      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18763      * you should override this method and provide a custom implementation.
18764      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18765      * @param {Event} e The event
18766      * @param {Object} data An object containing arbitrary data supplied by the drag source
18767      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18768      * underlying {@link Roo.dd.StatusProxy} can be updated
18769      */
18770     notifyEnter : function(dd, e, data){
18771         return this.dropNotAllowed;
18772     },
18773
18774     /**
18775      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18776      * This method will be called on every mouse movement while the drag source is over the drop zone.
18777      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18778      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18779      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18780      * registered node, it will call {@link #onContainerOver}.
18781      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18782      * @param {Event} e The event
18783      * @param {Object} data An object containing arbitrary data supplied by the drag source
18784      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18785      * underlying {@link Roo.dd.StatusProxy} can be updated
18786      */
18787     notifyOver : function(dd, e, data){
18788         var n = this.getTargetFromEvent(e);
18789         if(!n){ // not over valid drop target
18790             if(this.lastOverNode){
18791                 this.onNodeOut(this.lastOverNode, dd, e, data);
18792                 this.lastOverNode = null;
18793             }
18794             return this.onContainerOver(dd, e, data);
18795         }
18796         if(this.lastOverNode != n){
18797             if(this.lastOverNode){
18798                 this.onNodeOut(this.lastOverNode, dd, e, data);
18799             }
18800             this.onNodeEnter(n, dd, e, data);
18801             this.lastOverNode = n;
18802         }
18803         return this.onNodeOver(n, dd, e, data);
18804     },
18805
18806     /**
18807      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18808      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18809      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18810      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18811      * @param {Event} e The event
18812      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18813      */
18814     notifyOut : function(dd, e, data){
18815         if(this.lastOverNode){
18816             this.onNodeOut(this.lastOverNode, dd, e, data);
18817             this.lastOverNode = null;
18818         }
18819     },
18820
18821     /**
18822      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18823      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18824      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18825      * otherwise it will call {@link #onContainerDrop}.
18826      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18827      * @param {Event} e The event
18828      * @param {Object} data An object containing arbitrary data supplied by the drag source
18829      * @return {Boolean} True if the drop was valid, else false
18830      */
18831     notifyDrop : function(dd, e, data){
18832         if(this.lastOverNode){
18833             this.onNodeOut(this.lastOverNode, dd, e, data);
18834             this.lastOverNode = null;
18835         }
18836         var n = this.getTargetFromEvent(e);
18837         return n ?
18838             this.onNodeDrop(n, dd, e, data) :
18839             this.onContainerDrop(dd, e, data);
18840     },
18841
18842     // private
18843     triggerCacheRefresh : function(){
18844         Roo.dd.DDM.refreshCache(this.groups);
18845     }  
18846 });/*
18847  * Based on:
18848  * Ext JS Library 1.1.1
18849  * Copyright(c) 2006-2007, Ext JS, LLC.
18850  *
18851  * Originally Released Under LGPL - original licence link has changed is not relivant.
18852  *
18853  * Fork - LGPL
18854  * <script type="text/javascript">
18855  */
18856
18857
18858 /**
18859  * @class Roo.data.SortTypes
18860  * @singleton
18861  * Defines the default sorting (casting?) comparison functions used when sorting data.
18862  */
18863 Roo.data.SortTypes = {
18864     /**
18865      * Default sort that does nothing
18866      * @param {Mixed} s The value being converted
18867      * @return {Mixed} The comparison value
18868      */
18869     none : function(s){
18870         return s;
18871     },
18872     
18873     /**
18874      * The regular expression used to strip tags
18875      * @type {RegExp}
18876      * @property
18877      */
18878     stripTagsRE : /<\/?[^>]+>/gi,
18879     
18880     /**
18881      * Strips all HTML tags to sort on text only
18882      * @param {Mixed} s The value being converted
18883      * @return {String} The comparison value
18884      */
18885     asText : function(s){
18886         return String(s).replace(this.stripTagsRE, "");
18887     },
18888     
18889     /**
18890      * Strips all HTML tags to sort on text only - Case insensitive
18891      * @param {Mixed} s The value being converted
18892      * @return {String} The comparison value
18893      */
18894     asUCText : function(s){
18895         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18896     },
18897     
18898     /**
18899      * Case insensitive string
18900      * @param {Mixed} s The value being converted
18901      * @return {String} The comparison value
18902      */
18903     asUCString : function(s) {
18904         return String(s).toUpperCase();
18905     },
18906     
18907     /**
18908      * Date sorting
18909      * @param {Mixed} s The value being converted
18910      * @return {Number} The comparison value
18911      */
18912     asDate : function(s) {
18913         if(!s){
18914             return 0;
18915         }
18916         if(s instanceof Date){
18917             return s.getTime();
18918         }
18919         return Date.parse(String(s));
18920     },
18921     
18922     /**
18923      * Float sorting
18924      * @param {Mixed} s The value being converted
18925      * @return {Float} The comparison value
18926      */
18927     asFloat : function(s) {
18928         var val = parseFloat(String(s).replace(/,/g, ""));
18929         if(isNaN(val)) val = 0;
18930         return val;
18931     },
18932     
18933     /**
18934      * Integer sorting
18935      * @param {Mixed} s The value being converted
18936      * @return {Number} The comparison value
18937      */
18938     asInt : function(s) {
18939         var val = parseInt(String(s).replace(/,/g, ""));
18940         if(isNaN(val)) val = 0;
18941         return val;
18942     }
18943 };/*
18944  * Based on:
18945  * Ext JS Library 1.1.1
18946  * Copyright(c) 2006-2007, Ext JS, LLC.
18947  *
18948  * Originally Released Under LGPL - original licence link has changed is not relivant.
18949  *
18950  * Fork - LGPL
18951  * <script type="text/javascript">
18952  */
18953
18954 /**
18955 * @class Roo.data.Record
18956  * Instances of this class encapsulate both record <em>definition</em> information, and record
18957  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18958  * to access Records cached in an {@link Roo.data.Store} object.<br>
18959  * <p>
18960  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18961  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18962  * objects.<br>
18963  * <p>
18964  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18965  * @constructor
18966  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18967  * {@link #create}. The parameters are the same.
18968  * @param {Array} data An associative Array of data values keyed by the field name.
18969  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18970  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18971  * not specified an integer id is generated.
18972  */
18973 Roo.data.Record = function(data, id){
18974     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18975     this.data = data;
18976 };
18977
18978 /**
18979  * Generate a constructor for a specific record layout.
18980  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18981  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18982  * Each field definition object may contain the following properties: <ul>
18983  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
18984  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18985  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18986  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18987  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18988  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18989  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18990  * this may be omitted.</p></li>
18991  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18992  * <ul><li>auto (Default, implies no conversion)</li>
18993  * <li>string</li>
18994  * <li>int</li>
18995  * <li>float</li>
18996  * <li>boolean</li>
18997  * <li>date</li></ul></p></li>
18998  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18999  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
19000  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
19001  * by the Reader into an object that will be stored in the Record. It is passed the
19002  * following parameters:<ul>
19003  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
19004  * </ul></p></li>
19005  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
19006  * </ul>
19007  * <br>usage:<br><pre><code>
19008 var TopicRecord = Roo.data.Record.create(
19009     {name: 'title', mapping: 'topic_title'},
19010     {name: 'author', mapping: 'username'},
19011     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
19012     {name: 'lastPost', mapping: 'post_time', type: 'date'},
19013     {name: 'lastPoster', mapping: 'user2'},
19014     {name: 'excerpt', mapping: 'post_text'}
19015 );
19016
19017 var myNewRecord = new TopicRecord({
19018     title: 'Do my job please',
19019     author: 'noobie',
19020     totalPosts: 1,
19021     lastPost: new Date(),
19022     lastPoster: 'Animal',
19023     excerpt: 'No way dude!'
19024 });
19025 myStore.add(myNewRecord);
19026 </code></pre>
19027  * @method create
19028  * @static
19029  */
19030 Roo.data.Record.create = function(o){
19031     var f = function(){
19032         f.superclass.constructor.apply(this, arguments);
19033     };
19034     Roo.extend(f, Roo.data.Record);
19035     var p = f.prototype;
19036     p.fields = new Roo.util.MixedCollection(false, function(field){
19037         return field.name;
19038     });
19039     for(var i = 0, len = o.length; i < len; i++){
19040         p.fields.add(new Roo.data.Field(o[i]));
19041     }
19042     f.getField = function(name){
19043         return p.fields.get(name);  
19044     };
19045     return f;
19046 };
19047
19048 Roo.data.Record.AUTO_ID = 1000;
19049 Roo.data.Record.EDIT = 'edit';
19050 Roo.data.Record.REJECT = 'reject';
19051 Roo.data.Record.COMMIT = 'commit';
19052
19053 Roo.data.Record.prototype = {
19054     /**
19055      * Readonly flag - true if this record has been modified.
19056      * @type Boolean
19057      */
19058     dirty : false,
19059     editing : false,
19060     error: null,
19061     modified: null,
19062
19063     // private
19064     join : function(store){
19065         this.store = store;
19066     },
19067
19068     /**
19069      * Set the named field to the specified value.
19070      * @param {String} name The name of the field to set.
19071      * @param {Object} value The value to set the field to.
19072      */
19073     set : function(name, value){
19074         if(this.data[name] == value){
19075             return;
19076         }
19077         this.dirty = true;
19078         if(!this.modified){
19079             this.modified = {};
19080         }
19081         if(typeof this.modified[name] == 'undefined'){
19082             this.modified[name] = this.data[name];
19083         }
19084         this.data[name] = value;
19085         if(!this.editing && this.store){
19086             this.store.afterEdit(this);
19087         }       
19088     },
19089
19090     /**
19091      * Get the value of the named field.
19092      * @param {String} name The name of the field to get the value of.
19093      * @return {Object} The value of the field.
19094      */
19095     get : function(name){
19096         return this.data[name]; 
19097     },
19098
19099     // private
19100     beginEdit : function(){
19101         this.editing = true;
19102         this.modified = {}; 
19103     },
19104
19105     // private
19106     cancelEdit : function(){
19107         this.editing = false;
19108         delete this.modified;
19109     },
19110
19111     // private
19112     endEdit : function(){
19113         this.editing = false;
19114         if(this.dirty && this.store){
19115             this.store.afterEdit(this);
19116         }
19117     },
19118
19119     /**
19120      * Usually called by the {@link Roo.data.Store} which owns the Record.
19121      * Rejects all changes made to the Record since either creation, or the last commit operation.
19122      * Modified fields are reverted to their original values.
19123      * <p>
19124      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19125      * of reject operations.
19126      */
19127     reject : function(){
19128         var m = this.modified;
19129         for(var n in m){
19130             if(typeof m[n] != "function"){
19131                 this.data[n] = m[n];
19132             }
19133         }
19134         this.dirty = false;
19135         delete this.modified;
19136         this.editing = false;
19137         if(this.store){
19138             this.store.afterReject(this);
19139         }
19140     },
19141
19142     /**
19143      * Usually called by the {@link Roo.data.Store} which owns the Record.
19144      * Commits all changes made to the Record since either creation, or the last commit operation.
19145      * <p>
19146      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19147      * of commit operations.
19148      */
19149     commit : function(){
19150         this.dirty = false;
19151         delete this.modified;
19152         this.editing = false;
19153         if(this.store){
19154             this.store.afterCommit(this);
19155         }
19156     },
19157
19158     // private
19159     hasError : function(){
19160         return this.error != null;
19161     },
19162
19163     // private
19164     clearError : function(){
19165         this.error = null;
19166     },
19167
19168     /**
19169      * Creates a copy of this record.
19170      * @param {String} id (optional) A new record id if you don't want to use this record's id
19171      * @return {Record}
19172      */
19173     copy : function(newId) {
19174         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19175     }
19176 };/*
19177  * Based on:
19178  * Ext JS Library 1.1.1
19179  * Copyright(c) 2006-2007, Ext JS, LLC.
19180  *
19181  * Originally Released Under LGPL - original licence link has changed is not relivant.
19182  *
19183  * Fork - LGPL
19184  * <script type="text/javascript">
19185  */
19186
19187
19188
19189 /**
19190  * @class Roo.data.Store
19191  * @extends Roo.util.Observable
19192  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19193  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19194  * <p>
19195  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
19196  * has no knowledge of the format of the data returned by the Proxy.<br>
19197  * <p>
19198  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19199  * instances from the data object. These records are cached and made available through accessor functions.
19200  * @constructor
19201  * Creates a new Store.
19202  * @param {Object} config A config object containing the objects needed for the Store to access data,
19203  * and read the data into Records.
19204  */
19205 Roo.data.Store = function(config){
19206     this.data = new Roo.util.MixedCollection(false);
19207     this.data.getKey = function(o){
19208         return o.id;
19209     };
19210     this.baseParams = {};
19211     // private
19212     this.paramNames = {
19213         "start" : "start",
19214         "limit" : "limit",
19215         "sort" : "sort",
19216         "dir" : "dir",
19217         "multisort" : "_multisort"
19218     };
19219
19220     if(config && config.data){
19221         this.inlineData = config.data;
19222         delete config.data;
19223     }
19224
19225     Roo.apply(this, config);
19226     
19227     if(this.reader){ // reader passed
19228         this.reader = Roo.factory(this.reader, Roo.data);
19229         this.reader.xmodule = this.xmodule || false;
19230         if(!this.recordType){
19231             this.recordType = this.reader.recordType;
19232         }
19233         if(this.reader.onMetaChange){
19234             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19235         }
19236     }
19237
19238     if(this.recordType){
19239         this.fields = this.recordType.prototype.fields;
19240     }
19241     this.modified = [];
19242
19243     this.addEvents({
19244         /**
19245          * @event datachanged
19246          * Fires when the data cache has changed, and a widget which is using this Store
19247          * as a Record cache should refresh its view.
19248          * @param {Store} this
19249          */
19250         datachanged : true,
19251         /**
19252          * @event metachange
19253          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19254          * @param {Store} this
19255          * @param {Object} meta The JSON metadata
19256          */
19257         metachange : true,
19258         /**
19259          * @event add
19260          * Fires when Records have been added to the Store
19261          * @param {Store} this
19262          * @param {Roo.data.Record[]} records The array of Records added
19263          * @param {Number} index The index at which the record(s) were added
19264          */
19265         add : true,
19266         /**
19267          * @event remove
19268          * Fires when a Record has been removed from the Store
19269          * @param {Store} this
19270          * @param {Roo.data.Record} record The Record that was removed
19271          * @param {Number} index The index at which the record was removed
19272          */
19273         remove : true,
19274         /**
19275          * @event update
19276          * Fires when a Record has been updated
19277          * @param {Store} this
19278          * @param {Roo.data.Record} record The Record that was updated
19279          * @param {String} operation The update operation being performed.  Value may be one of:
19280          * <pre><code>
19281  Roo.data.Record.EDIT
19282  Roo.data.Record.REJECT
19283  Roo.data.Record.COMMIT
19284          * </code></pre>
19285          */
19286         update : true,
19287         /**
19288          * @event clear
19289          * Fires when the data cache has been cleared.
19290          * @param {Store} this
19291          */
19292         clear : true,
19293         /**
19294          * @event beforeload
19295          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19296          * the load action will be canceled.
19297          * @param {Store} this
19298          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19299          */
19300         beforeload : true,
19301         /**
19302          * @event load
19303          * Fires after a new set of Records has been loaded.
19304          * @param {Store} this
19305          * @param {Roo.data.Record[]} records The Records that were loaded
19306          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19307          */
19308         load : true,
19309         /**
19310          * @event loadexception
19311          * Fires if an exception occurs in the Proxy during loading.
19312          * Called with the signature of the Proxy's "loadexception" event.
19313          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19314          * 
19315          * @param {Proxy} 
19316          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19317          * @param {Object} load options 
19318          * @param {Object} jsonData from your request (normally this contains the Exception)
19319          */
19320         loadexception : true
19321     });
19322     
19323     if(this.proxy){
19324         this.proxy = Roo.factory(this.proxy, Roo.data);
19325         this.proxy.xmodule = this.xmodule || false;
19326         this.relayEvents(this.proxy,  ["loadexception"]);
19327     }
19328     this.sortToggle = {};
19329     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19330
19331     Roo.data.Store.superclass.constructor.call(this);
19332
19333     if(this.inlineData){
19334         this.loadData(this.inlineData);
19335         delete this.inlineData;
19336     }
19337 };
19338 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19339      /**
19340     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19341     * without a remote query - used by combo/forms at present.
19342     */
19343     
19344     /**
19345     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19346     */
19347     /**
19348     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19349     */
19350     /**
19351     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19352     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19353     */
19354     /**
19355     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19356     * on any HTTP request
19357     */
19358     /**
19359     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19360     */
19361     /**
19362     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19363     */
19364     multiSort: false,
19365     /**
19366     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19367     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19368     */
19369     remoteSort : false,
19370
19371     /**
19372     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19373      * loaded or when a record is removed. (defaults to false).
19374     */
19375     pruneModifiedRecords : false,
19376
19377     // private
19378     lastOptions : null,
19379
19380     /**
19381      * Add Records to the Store and fires the add event.
19382      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19383      */
19384     add : function(records){
19385         records = [].concat(records);
19386         for(var i = 0, len = records.length; i < len; i++){
19387             records[i].join(this);
19388         }
19389         var index = this.data.length;
19390         this.data.addAll(records);
19391         this.fireEvent("add", this, records, index);
19392     },
19393
19394     /**
19395      * Remove a Record from the Store and fires the remove event.
19396      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19397      */
19398     remove : function(record){
19399         var index = this.data.indexOf(record);
19400         this.data.removeAt(index);
19401         if(this.pruneModifiedRecords){
19402             this.modified.remove(record);
19403         }
19404         this.fireEvent("remove", this, record, index);
19405     },
19406
19407     /**
19408      * Remove all Records from the Store and fires the clear event.
19409      */
19410     removeAll : function(){
19411         this.data.clear();
19412         if(this.pruneModifiedRecords){
19413             this.modified = [];
19414         }
19415         this.fireEvent("clear", this);
19416     },
19417
19418     /**
19419      * Inserts Records to the Store at the given index and fires the add event.
19420      * @param {Number} index The start index at which to insert the passed Records.
19421      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19422      */
19423     insert : function(index, records){
19424         records = [].concat(records);
19425         for(var i = 0, len = records.length; i < len; i++){
19426             this.data.insert(index, records[i]);
19427             records[i].join(this);
19428         }
19429         this.fireEvent("add", this, records, index);
19430     },
19431
19432     /**
19433      * Get the index within the cache of the passed Record.
19434      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19435      * @return {Number} The index of the passed Record. Returns -1 if not found.
19436      */
19437     indexOf : function(record){
19438         return this.data.indexOf(record);
19439     },
19440
19441     /**
19442      * Get the index within the cache of the Record with the passed id.
19443      * @param {String} id The id of the Record to find.
19444      * @return {Number} The index of the Record. Returns -1 if not found.
19445      */
19446     indexOfId : function(id){
19447         return this.data.indexOfKey(id);
19448     },
19449
19450     /**
19451      * Get the Record with the specified id.
19452      * @param {String} id The id of the Record to find.
19453      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19454      */
19455     getById : function(id){
19456         return this.data.key(id);
19457     },
19458
19459     /**
19460      * Get the Record at the specified index.
19461      * @param {Number} index The index of the Record to find.
19462      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19463      */
19464     getAt : function(index){
19465         return this.data.itemAt(index);
19466     },
19467
19468     /**
19469      * Returns a range of Records between specified indices.
19470      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19471      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19472      * @return {Roo.data.Record[]} An array of Records
19473      */
19474     getRange : function(start, end){
19475         return this.data.getRange(start, end);
19476     },
19477
19478     // private
19479     storeOptions : function(o){
19480         o = Roo.apply({}, o);
19481         delete o.callback;
19482         delete o.scope;
19483         this.lastOptions = o;
19484     },
19485
19486     /**
19487      * Loads the Record cache from the configured Proxy using the configured Reader.
19488      * <p>
19489      * If using remote paging, then the first load call must specify the <em>start</em>
19490      * and <em>limit</em> properties in the options.params property to establish the initial
19491      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19492      * <p>
19493      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19494      * and this call will return before the new data has been loaded. Perform any post-processing
19495      * in a callback function, or in a "load" event handler.</strong>
19496      * <p>
19497      * @param {Object} options An object containing properties which control loading options:<ul>
19498      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19499      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19500      * passed the following arguments:<ul>
19501      * <li>r : Roo.data.Record[]</li>
19502      * <li>options: Options object from the load call</li>
19503      * <li>success: Boolean success indicator</li></ul></li>
19504      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19505      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19506      * </ul>
19507      */
19508     load : function(options){
19509         options = options || {};
19510         if(this.fireEvent("beforeload", this, options) !== false){
19511             this.storeOptions(options);
19512             var p = Roo.apply(options.params || {}, this.baseParams);
19513             // if meta was not loaded from remote source.. try requesting it.
19514             if (!this.reader.metaFromRemote) {
19515                 p._requestMeta = 1;
19516             }
19517             if(this.sortInfo && this.remoteSort){
19518                 var pn = this.paramNames;
19519                 p[pn["sort"]] = this.sortInfo.field;
19520                 p[pn["dir"]] = this.sortInfo.direction;
19521             }
19522             if (this.multiSort) {
19523                 var pn = this.paramNames;
19524                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19525             }
19526             
19527             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19528         }
19529     },
19530
19531     /**
19532      * Reloads the Record cache from the configured Proxy using the configured Reader and
19533      * the options from the last load operation performed.
19534      * @param {Object} options (optional) An object containing properties which may override the options
19535      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19536      * the most recently used options are reused).
19537      */
19538     reload : function(options){
19539         this.load(Roo.applyIf(options||{}, this.lastOptions));
19540     },
19541
19542     // private
19543     // Called as a callback by the Reader during a load operation.
19544     loadRecords : function(o, options, success){
19545         if(!o || success === false){
19546             if(success !== false){
19547                 this.fireEvent("load", this, [], options);
19548             }
19549             if(options.callback){
19550                 options.callback.call(options.scope || this, [], options, false);
19551             }
19552             return;
19553         }
19554         // if data returned failure - throw an exception.
19555         if (o.success === false) {
19556             // show a message if no listener is registered.
19557             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
19558                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
19559             }
19560             // loadmask wil be hooked into this..
19561             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19562             return;
19563         }
19564         var r = o.records, t = o.totalRecords || r.length;
19565         if(!options || options.add !== true){
19566             if(this.pruneModifiedRecords){
19567                 this.modified = [];
19568             }
19569             for(var i = 0, len = r.length; i < len; i++){
19570                 r[i].join(this);
19571             }
19572             if(this.snapshot){
19573                 this.data = this.snapshot;
19574                 delete this.snapshot;
19575             }
19576             this.data.clear();
19577             this.data.addAll(r);
19578             this.totalLength = t;
19579             this.applySort();
19580             this.fireEvent("datachanged", this);
19581         }else{
19582             this.totalLength = Math.max(t, this.data.length+r.length);
19583             this.add(r);
19584         }
19585         this.fireEvent("load", this, r, options);
19586         if(options.callback){
19587             options.callback.call(options.scope || this, r, options, true);
19588         }
19589     },
19590
19591
19592     /**
19593      * Loads data from a passed data block. A Reader which understands the format of the data
19594      * must have been configured in the constructor.
19595      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19596      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19597      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19598      */
19599     loadData : function(o, append){
19600         var r = this.reader.readRecords(o);
19601         this.loadRecords(r, {add: append}, true);
19602     },
19603
19604     /**
19605      * Gets the number of cached records.
19606      * <p>
19607      * <em>If using paging, this may not be the total size of the dataset. If the data object
19608      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19609      * the data set size</em>
19610      */
19611     getCount : function(){
19612         return this.data.length || 0;
19613     },
19614
19615     /**
19616      * Gets the total number of records in the dataset as returned by the server.
19617      * <p>
19618      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19619      * the dataset size</em>
19620      */
19621     getTotalCount : function(){
19622         return this.totalLength || 0;
19623     },
19624
19625     /**
19626      * Returns the sort state of the Store as an object with two properties:
19627      * <pre><code>
19628  field {String} The name of the field by which the Records are sorted
19629  direction {String} The sort order, "ASC" or "DESC"
19630      * </code></pre>
19631      */
19632     getSortState : function(){
19633         return this.sortInfo;
19634     },
19635
19636     // private
19637     applySort : function(){
19638         if(this.sortInfo && !this.remoteSort){
19639             var s = this.sortInfo, f = s.field;
19640             var st = this.fields.get(f).sortType;
19641             var fn = function(r1, r2){
19642                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19643                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19644             };
19645             this.data.sort(s.direction, fn);
19646             if(this.snapshot && this.snapshot != this.data){
19647                 this.snapshot.sort(s.direction, fn);
19648             }
19649         }
19650     },
19651
19652     /**
19653      * Sets the default sort column and order to be used by the next load operation.
19654      * @param {String} fieldName The name of the field to sort by.
19655      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19656      */
19657     setDefaultSort : function(field, dir){
19658         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19659     },
19660
19661     /**
19662      * Sort the Records.
19663      * If remote sorting is used, the sort is performed on the server, and the cache is
19664      * reloaded. If local sorting is used, the cache is sorted internally.
19665      * @param {String} fieldName The name of the field to sort by.
19666      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19667      */
19668     sort : function(fieldName, dir){
19669         var f = this.fields.get(fieldName);
19670         if(!dir){
19671             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19672             
19673             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19674                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19675             }else{
19676                 dir = f.sortDir;
19677             }
19678         }
19679         this.sortToggle[f.name] = dir;
19680         this.sortInfo = {field: f.name, direction: dir};
19681         if(!this.remoteSort){
19682             this.applySort();
19683             this.fireEvent("datachanged", this);
19684         }else{
19685             this.load(this.lastOptions);
19686         }
19687     },
19688
19689     /**
19690      * Calls the specified function for each of the Records in the cache.
19691      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19692      * Returning <em>false</em> aborts and exits the iteration.
19693      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19694      */
19695     each : function(fn, scope){
19696         this.data.each(fn, scope);
19697     },
19698
19699     /**
19700      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19701      * (e.g., during paging).
19702      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19703      */
19704     getModifiedRecords : function(){
19705         return this.modified;
19706     },
19707
19708     // private
19709     createFilterFn : function(property, value, anyMatch){
19710         if(!value.exec){ // not a regex
19711             value = String(value);
19712             if(value.length == 0){
19713                 return false;
19714             }
19715             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19716         }
19717         return function(r){
19718             return value.test(r.data[property]);
19719         };
19720     },
19721
19722     /**
19723      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19724      * @param {String} property A field on your records
19725      * @param {Number} start The record index to start at (defaults to 0)
19726      * @param {Number} end The last record index to include (defaults to length - 1)
19727      * @return {Number} The sum
19728      */
19729     sum : function(property, start, end){
19730         var rs = this.data.items, v = 0;
19731         start = start || 0;
19732         end = (end || end === 0) ? end : rs.length-1;
19733
19734         for(var i = start; i <= end; i++){
19735             v += (rs[i].data[property] || 0);
19736         }
19737         return v;
19738     },
19739
19740     /**
19741      * Filter the records by a specified property.
19742      * @param {String} field A field on your records
19743      * @param {String/RegExp} value Either a string that the field
19744      * should start with or a RegExp to test against the field
19745      * @param {Boolean} anyMatch True to match any part not just the beginning
19746      */
19747     filter : function(property, value, anyMatch){
19748         var fn = this.createFilterFn(property, value, anyMatch);
19749         return fn ? this.filterBy(fn) : this.clearFilter();
19750     },
19751
19752     /**
19753      * Filter by a function. The specified function will be called with each
19754      * record in this data source. If the function returns true the record is included,
19755      * otherwise it is filtered.
19756      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19757      * @param {Object} scope (optional) The scope of the function (defaults to this)
19758      */
19759     filterBy : function(fn, scope){
19760         this.snapshot = this.snapshot || this.data;
19761         this.data = this.queryBy(fn, scope||this);
19762         this.fireEvent("datachanged", this);
19763     },
19764
19765     /**
19766      * Query the records by a specified property.
19767      * @param {String} field A field on your records
19768      * @param {String/RegExp} value Either a string that the field
19769      * should start with or a RegExp to test against the field
19770      * @param {Boolean} anyMatch True to match any part not just the beginning
19771      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19772      */
19773     query : function(property, value, anyMatch){
19774         var fn = this.createFilterFn(property, value, anyMatch);
19775         return fn ? this.queryBy(fn) : this.data.clone();
19776     },
19777
19778     /**
19779      * Query by a function. The specified function will be called with each
19780      * record in this data source. If the function returns true the record is included
19781      * in the results.
19782      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19783      * @param {Object} scope (optional) The scope of the function (defaults to this)
19784       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19785      **/
19786     queryBy : function(fn, scope){
19787         var data = this.snapshot || this.data;
19788         return data.filterBy(fn, scope||this);
19789     },
19790
19791     /**
19792      * Collects unique values for a particular dataIndex from this store.
19793      * @param {String} dataIndex The property to collect
19794      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19795      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19796      * @return {Array} An array of the unique values
19797      **/
19798     collect : function(dataIndex, allowNull, bypassFilter){
19799         var d = (bypassFilter === true && this.snapshot) ?
19800                 this.snapshot.items : this.data.items;
19801         var v, sv, r = [], l = {};
19802         for(var i = 0, len = d.length; i < len; i++){
19803             v = d[i].data[dataIndex];
19804             sv = String(v);
19805             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19806                 l[sv] = true;
19807                 r[r.length] = v;
19808             }
19809         }
19810         return r;
19811     },
19812
19813     /**
19814      * Revert to a view of the Record cache with no filtering applied.
19815      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19816      */
19817     clearFilter : function(suppressEvent){
19818         if(this.snapshot && this.snapshot != this.data){
19819             this.data = this.snapshot;
19820             delete this.snapshot;
19821             if(suppressEvent !== true){
19822                 this.fireEvent("datachanged", this);
19823             }
19824         }
19825     },
19826
19827     // private
19828     afterEdit : function(record){
19829         if(this.modified.indexOf(record) == -1){
19830             this.modified.push(record);
19831         }
19832         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19833     },
19834     
19835     // private
19836     afterReject : function(record){
19837         this.modified.remove(record);
19838         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19839     },
19840
19841     // private
19842     afterCommit : function(record){
19843         this.modified.remove(record);
19844         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19845     },
19846
19847     /**
19848      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19849      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19850      */
19851     commitChanges : function(){
19852         var m = this.modified.slice(0);
19853         this.modified = [];
19854         for(var i = 0, len = m.length; i < len; i++){
19855             m[i].commit();
19856         }
19857     },
19858
19859     /**
19860      * Cancel outstanding changes on all changed records.
19861      */
19862     rejectChanges : function(){
19863         var m = this.modified.slice(0);
19864         this.modified = [];
19865         for(var i = 0, len = m.length; i < len; i++){
19866             m[i].reject();
19867         }
19868     },
19869
19870     onMetaChange : function(meta, rtype, o){
19871         this.recordType = rtype;
19872         this.fields = rtype.prototype.fields;
19873         delete this.snapshot;
19874         this.sortInfo = meta.sortInfo || this.sortInfo;
19875         this.modified = [];
19876         this.fireEvent('metachange', this, this.reader.meta);
19877     }
19878 });/*
19879  * Based on:
19880  * Ext JS Library 1.1.1
19881  * Copyright(c) 2006-2007, Ext JS, LLC.
19882  *
19883  * Originally Released Under LGPL - original licence link has changed is not relivant.
19884  *
19885  * Fork - LGPL
19886  * <script type="text/javascript">
19887  */
19888
19889 /**
19890  * @class Roo.data.SimpleStore
19891  * @extends Roo.data.Store
19892  * Small helper class to make creating Stores from Array data easier.
19893  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19894  * @cfg {Array} fields An array of field definition objects, or field name strings.
19895  * @cfg {Array} data The multi-dimensional array of data
19896  * @constructor
19897  * @param {Object} config
19898  */
19899 Roo.data.SimpleStore = function(config){
19900     Roo.data.SimpleStore.superclass.constructor.call(this, {
19901         isLocal : true,
19902         reader: new Roo.data.ArrayReader({
19903                 id: config.id
19904             },
19905             Roo.data.Record.create(config.fields)
19906         ),
19907         proxy : new Roo.data.MemoryProxy(config.data)
19908     });
19909     this.load();
19910 };
19911 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19912  * Based on:
19913  * Ext JS Library 1.1.1
19914  * Copyright(c) 2006-2007, Ext JS, LLC.
19915  *
19916  * Originally Released Under LGPL - original licence link has changed is not relivant.
19917  *
19918  * Fork - LGPL
19919  * <script type="text/javascript">
19920  */
19921
19922 /**
19923 /**
19924  * @extends Roo.data.Store
19925  * @class Roo.data.JsonStore
19926  * Small helper class to make creating Stores for JSON data easier. <br/>
19927 <pre><code>
19928 var store = new Roo.data.JsonStore({
19929     url: 'get-images.php',
19930     root: 'images',
19931     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19932 });
19933 </code></pre>
19934  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19935  * JsonReader and HttpProxy (unless inline data is provided).</b>
19936  * @cfg {Array} fields An array of field definition objects, or field name strings.
19937  * @constructor
19938  * @param {Object} config
19939  */
19940 Roo.data.JsonStore = function(c){
19941     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19942         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19943         reader: new Roo.data.JsonReader(c, c.fields)
19944     }));
19945 };
19946 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19947  * Based on:
19948  * Ext JS Library 1.1.1
19949  * Copyright(c) 2006-2007, Ext JS, LLC.
19950  *
19951  * Originally Released Under LGPL - original licence link has changed is not relivant.
19952  *
19953  * Fork - LGPL
19954  * <script type="text/javascript">
19955  */
19956
19957  
19958 Roo.data.Field = function(config){
19959     if(typeof config == "string"){
19960         config = {name: config};
19961     }
19962     Roo.apply(this, config);
19963     
19964     if(!this.type){
19965         this.type = "auto";
19966     }
19967     
19968     var st = Roo.data.SortTypes;
19969     // named sortTypes are supported, here we look them up
19970     if(typeof this.sortType == "string"){
19971         this.sortType = st[this.sortType];
19972     }
19973     
19974     // set default sortType for strings and dates
19975     if(!this.sortType){
19976         switch(this.type){
19977             case "string":
19978                 this.sortType = st.asUCString;
19979                 break;
19980             case "date":
19981                 this.sortType = st.asDate;
19982                 break;
19983             default:
19984                 this.sortType = st.none;
19985         }
19986     }
19987
19988     // define once
19989     var stripRe = /[\$,%]/g;
19990
19991     // prebuilt conversion function for this field, instead of
19992     // switching every time we're reading a value
19993     if(!this.convert){
19994         var cv, dateFormat = this.dateFormat;
19995         switch(this.type){
19996             case "":
19997             case "auto":
19998             case undefined:
19999                 cv = function(v){ return v; };
20000                 break;
20001             case "string":
20002                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
20003                 break;
20004             case "int":
20005                 cv = function(v){
20006                     return v !== undefined && v !== null && v !== '' ?
20007                            parseInt(String(v).replace(stripRe, ""), 10) : '';
20008                     };
20009                 break;
20010             case "float":
20011                 cv = function(v){
20012                     return v !== undefined && v !== null && v !== '' ?
20013                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
20014                     };
20015                 break;
20016             case "bool":
20017             case "boolean":
20018                 cv = function(v){ return v === true || v === "true" || v == 1; };
20019                 break;
20020             case "date":
20021                 cv = function(v){
20022                     if(!v){
20023                         return '';
20024                     }
20025                     if(v instanceof Date){
20026                         return v;
20027                     }
20028                     if(dateFormat){
20029                         if(dateFormat == "timestamp"){
20030                             return new Date(v*1000);
20031                         }
20032                         return Date.parseDate(v, dateFormat);
20033                     }
20034                     var parsed = Date.parse(v);
20035                     return parsed ? new Date(parsed) : null;
20036                 };
20037              break;
20038             
20039         }
20040         this.convert = cv;
20041     }
20042 };
20043
20044 Roo.data.Field.prototype = {
20045     dateFormat: null,
20046     defaultValue: "",
20047     mapping: null,
20048     sortType : null,
20049     sortDir : "ASC"
20050 };/*
20051  * Based on:
20052  * Ext JS Library 1.1.1
20053  * Copyright(c) 2006-2007, Ext JS, LLC.
20054  *
20055  * Originally Released Under LGPL - original licence link has changed is not relivant.
20056  *
20057  * Fork - LGPL
20058  * <script type="text/javascript">
20059  */
20060  
20061 // Base class for reading structured data from a data source.  This class is intended to be
20062 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20063
20064 /**
20065  * @class Roo.data.DataReader
20066  * Base class for reading structured data from a data source.  This class is intended to be
20067  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20068  */
20069
20070 Roo.data.DataReader = function(meta, recordType){
20071     
20072     this.meta = meta;
20073     
20074     this.recordType = recordType instanceof Array ? 
20075         Roo.data.Record.create(recordType) : recordType;
20076 };
20077
20078 Roo.data.DataReader.prototype = {
20079      /**
20080      * Create an empty record
20081      * @param {Object} data (optional) - overlay some values
20082      * @return {Roo.data.Record} record created.
20083      */
20084     newRow :  function(d) {
20085         var da =  {};
20086         this.recordType.prototype.fields.each(function(c) {
20087             switch( c.type) {
20088                 case 'int' : da[c.name] = 0; break;
20089                 case 'date' : da[c.name] = new Date(); break;
20090                 case 'float' : da[c.name] = 0.0; break;
20091                 case 'boolean' : da[c.name] = false; break;
20092                 default : da[c.name] = ""; break;
20093             }
20094             
20095         });
20096         return new this.recordType(Roo.apply(da, d));
20097     }
20098     
20099 };/*
20100  * Based on:
20101  * Ext JS Library 1.1.1
20102  * Copyright(c) 2006-2007, Ext JS, LLC.
20103  *
20104  * Originally Released Under LGPL - original licence link has changed is not relivant.
20105  *
20106  * Fork - LGPL
20107  * <script type="text/javascript">
20108  */
20109
20110 /**
20111  * @class Roo.data.DataProxy
20112  * @extends Roo.data.Observable
20113  * This class is an abstract base class for implementations which provide retrieval of
20114  * unformatted data objects.<br>
20115  * <p>
20116  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20117  * (of the appropriate type which knows how to parse the data object) to provide a block of
20118  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20119  * <p>
20120  * Custom implementations must implement the load method as described in
20121  * {@link Roo.data.HttpProxy#load}.
20122  */
20123 Roo.data.DataProxy = function(){
20124     this.addEvents({
20125         /**
20126          * @event beforeload
20127          * Fires before a network request is made to retrieve a data object.
20128          * @param {Object} This DataProxy object.
20129          * @param {Object} params The params parameter to the load function.
20130          */
20131         beforeload : true,
20132         /**
20133          * @event load
20134          * Fires before the load method's callback is called.
20135          * @param {Object} This DataProxy object.
20136          * @param {Object} o The data object.
20137          * @param {Object} arg The callback argument object passed to the load function.
20138          */
20139         load : true,
20140         /**
20141          * @event loadexception
20142          * Fires if an Exception occurs during data retrieval.
20143          * @param {Object} This DataProxy object.
20144          * @param {Object} o The data object.
20145          * @param {Object} arg The callback argument object passed to the load function.
20146          * @param {Object} e The Exception.
20147          */
20148         loadexception : true
20149     });
20150     Roo.data.DataProxy.superclass.constructor.call(this);
20151 };
20152
20153 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20154
20155     /**
20156      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20157      */
20158 /*
20159  * Based on:
20160  * Ext JS Library 1.1.1
20161  * Copyright(c) 2006-2007, Ext JS, LLC.
20162  *
20163  * Originally Released Under LGPL - original licence link has changed is not relivant.
20164  *
20165  * Fork - LGPL
20166  * <script type="text/javascript">
20167  */
20168 /**
20169  * @class Roo.data.MemoryProxy
20170  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20171  * to the Reader when its load method is called.
20172  * @constructor
20173  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20174  */
20175 Roo.data.MemoryProxy = function(data){
20176     if (data.data) {
20177         data = data.data;
20178     }
20179     Roo.data.MemoryProxy.superclass.constructor.call(this);
20180     this.data = data;
20181 };
20182
20183 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20184     /**
20185      * Load data from the requested source (in this case an in-memory
20186      * data object passed to the constructor), read the data object into
20187      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20188      * process that block using the passed callback.
20189      * @param {Object} params This parameter is not used by the MemoryProxy class.
20190      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20191      * object into a block of Roo.data.Records.
20192      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20193      * The function must be passed <ul>
20194      * <li>The Record block object</li>
20195      * <li>The "arg" argument from the load function</li>
20196      * <li>A boolean success indicator</li>
20197      * </ul>
20198      * @param {Object} scope The scope in which to call the callback
20199      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20200      */
20201     load : function(params, reader, callback, scope, arg){
20202         params = params || {};
20203         var result;
20204         try {
20205             result = reader.readRecords(this.data);
20206         }catch(e){
20207             this.fireEvent("loadexception", this, arg, null, e);
20208             callback.call(scope, null, arg, false);
20209             return;
20210         }
20211         callback.call(scope, result, arg, true);
20212     },
20213     
20214     // private
20215     update : function(params, records){
20216         
20217     }
20218 });/*
20219  * Based on:
20220  * Ext JS Library 1.1.1
20221  * Copyright(c) 2006-2007, Ext JS, LLC.
20222  *
20223  * Originally Released Under LGPL - original licence link has changed is not relivant.
20224  *
20225  * Fork - LGPL
20226  * <script type="text/javascript">
20227  */
20228 /**
20229  * @class Roo.data.HttpProxy
20230  * @extends Roo.data.DataProxy
20231  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20232  * configured to reference a certain URL.<br><br>
20233  * <p>
20234  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20235  * from which the running page was served.<br><br>
20236  * <p>
20237  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20238  * <p>
20239  * Be aware that to enable the browser to parse an XML document, the server must set
20240  * the Content-Type header in the HTTP response to "text/xml".
20241  * @constructor
20242  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20243  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20244  * will be used to make the request.
20245  */
20246 Roo.data.HttpProxy = function(conn){
20247     Roo.data.HttpProxy.superclass.constructor.call(this);
20248     // is conn a conn config or a real conn?
20249     this.conn = conn;
20250     this.useAjax = !conn || !conn.events;
20251   
20252 };
20253
20254 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20255     // thse are take from connection...
20256     
20257     /**
20258      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20259      */
20260     /**
20261      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20262      * extra parameters to each request made by this object. (defaults to undefined)
20263      */
20264     /**
20265      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20266      *  to each request made by this object. (defaults to undefined)
20267      */
20268     /**
20269      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
20270      */
20271     /**
20272      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20273      */
20274      /**
20275      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20276      * @type Boolean
20277      */
20278   
20279
20280     /**
20281      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20282      * @type Boolean
20283      */
20284     /**
20285      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20286      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20287      * a finer-grained basis than the DataProxy events.
20288      */
20289     getConnection : function(){
20290         return this.useAjax ? Roo.Ajax : this.conn;
20291     },
20292
20293     /**
20294      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20295      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20296      * process that block using the passed callback.
20297      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20298      * for the request to the remote server.
20299      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20300      * object into a block of Roo.data.Records.
20301      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20302      * The function must be passed <ul>
20303      * <li>The Record block object</li>
20304      * <li>The "arg" argument from the load function</li>
20305      * <li>A boolean success indicator</li>
20306      * </ul>
20307      * @param {Object} scope The scope in which to call the callback
20308      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20309      */
20310     load : function(params, reader, callback, scope, arg){
20311         if(this.fireEvent("beforeload", this, params) !== false){
20312             var  o = {
20313                 params : params || {},
20314                 request: {
20315                     callback : callback,
20316                     scope : scope,
20317                     arg : arg
20318                 },
20319                 reader: reader,
20320                 callback : this.loadResponse,
20321                 scope: this
20322             };
20323             if(this.useAjax){
20324                 Roo.applyIf(o, this.conn);
20325                 if(this.activeRequest){
20326                     Roo.Ajax.abort(this.activeRequest);
20327                 }
20328                 this.activeRequest = Roo.Ajax.request(o);
20329             }else{
20330                 this.conn.request(o);
20331             }
20332         }else{
20333             callback.call(scope||this, null, arg, false);
20334         }
20335     },
20336
20337     // private
20338     loadResponse : function(o, success, response){
20339         delete this.activeRequest;
20340         if(!success){
20341             this.fireEvent("loadexception", this, o, response);
20342             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20343             return;
20344         }
20345         var result;
20346         try {
20347             result = o.reader.read(response);
20348         }catch(e){
20349             this.fireEvent("loadexception", this, o, response, e);
20350             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20351             return;
20352         }
20353         
20354         this.fireEvent("load", this, o, o.request.arg);
20355         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20356     },
20357
20358     // private
20359     update : function(dataSet){
20360
20361     },
20362
20363     // private
20364     updateResponse : function(dataSet){
20365
20366     }
20367 });/*
20368  * Based on:
20369  * Ext JS Library 1.1.1
20370  * Copyright(c) 2006-2007, Ext JS, LLC.
20371  *
20372  * Originally Released Under LGPL - original licence link has changed is not relivant.
20373  *
20374  * Fork - LGPL
20375  * <script type="text/javascript">
20376  */
20377
20378 /**
20379  * @class Roo.data.ScriptTagProxy
20380  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20381  * other than the originating domain of the running page.<br><br>
20382  * <p>
20383  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
20384  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20385  * <p>
20386  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20387  * source code that is used as the source inside a &lt;script> tag.<br><br>
20388  * <p>
20389  * In order for the browser to process the returned data, the server must wrap the data object
20390  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20391  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20392  * depending on whether the callback name was passed:
20393  * <p>
20394  * <pre><code>
20395 boolean scriptTag = false;
20396 String cb = request.getParameter("callback");
20397 if (cb != null) {
20398     scriptTag = true;
20399     response.setContentType("text/javascript");
20400 } else {
20401     response.setContentType("application/x-json");
20402 }
20403 Writer out = response.getWriter();
20404 if (scriptTag) {
20405     out.write(cb + "(");
20406 }
20407 out.print(dataBlock.toJsonString());
20408 if (scriptTag) {
20409     out.write(");");
20410 }
20411 </pre></code>
20412  *
20413  * @constructor
20414  * @param {Object} config A configuration object.
20415  */
20416 Roo.data.ScriptTagProxy = function(config){
20417     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20418     Roo.apply(this, config);
20419     this.head = document.getElementsByTagName("head")[0];
20420 };
20421
20422 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20423
20424 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20425     /**
20426      * @cfg {String} url The URL from which to request the data object.
20427      */
20428     /**
20429      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20430      */
20431     timeout : 30000,
20432     /**
20433      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20434      * the server the name of the callback function set up by the load call to process the returned data object.
20435      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20436      * javascript output which calls this named function passing the data object as its only parameter.
20437      */
20438     callbackParam : "callback",
20439     /**
20440      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20441      * name to the request.
20442      */
20443     nocache : true,
20444
20445     /**
20446      * Load data from the configured URL, read the data object into
20447      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20448      * process that block using the passed callback.
20449      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20450      * for the request to the remote server.
20451      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20452      * object into a block of Roo.data.Records.
20453      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20454      * The function must be passed <ul>
20455      * <li>The Record block object</li>
20456      * <li>The "arg" argument from the load function</li>
20457      * <li>A boolean success indicator</li>
20458      * </ul>
20459      * @param {Object} scope The scope in which to call the callback
20460      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20461      */
20462     load : function(params, reader, callback, scope, arg){
20463         if(this.fireEvent("beforeload", this, params) !== false){
20464
20465             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20466
20467             var url = this.url;
20468             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20469             if(this.nocache){
20470                 url += "&_dc=" + (new Date().getTime());
20471             }
20472             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20473             var trans = {
20474                 id : transId,
20475                 cb : "stcCallback"+transId,
20476                 scriptId : "stcScript"+transId,
20477                 params : params,
20478                 arg : arg,
20479                 url : url,
20480                 callback : callback,
20481                 scope : scope,
20482                 reader : reader
20483             };
20484             var conn = this;
20485
20486             window[trans.cb] = function(o){
20487                 conn.handleResponse(o, trans);
20488             };
20489
20490             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20491
20492             if(this.autoAbort !== false){
20493                 this.abort();
20494             }
20495
20496             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20497
20498             var script = document.createElement("script");
20499             script.setAttribute("src", url);
20500             script.setAttribute("type", "text/javascript");
20501             script.setAttribute("id", trans.scriptId);
20502             this.head.appendChild(script);
20503
20504             this.trans = trans;
20505         }else{
20506             callback.call(scope||this, null, arg, false);
20507         }
20508     },
20509
20510     // private
20511     isLoading : function(){
20512         return this.trans ? true : false;
20513     },
20514
20515     /**
20516      * Abort the current server request.
20517      */
20518     abort : function(){
20519         if(this.isLoading()){
20520             this.destroyTrans(this.trans);
20521         }
20522     },
20523
20524     // private
20525     destroyTrans : function(trans, isLoaded){
20526         this.head.removeChild(document.getElementById(trans.scriptId));
20527         clearTimeout(trans.timeoutId);
20528         if(isLoaded){
20529             window[trans.cb] = undefined;
20530             try{
20531                 delete window[trans.cb];
20532             }catch(e){}
20533         }else{
20534             // if hasn't been loaded, wait for load to remove it to prevent script error
20535             window[trans.cb] = function(){
20536                 window[trans.cb] = undefined;
20537                 try{
20538                     delete window[trans.cb];
20539                 }catch(e){}
20540             };
20541         }
20542     },
20543
20544     // private
20545     handleResponse : function(o, trans){
20546         this.trans = false;
20547         this.destroyTrans(trans, true);
20548         var result;
20549         try {
20550             result = trans.reader.readRecords(o);
20551         }catch(e){
20552             this.fireEvent("loadexception", this, o, trans.arg, e);
20553             trans.callback.call(trans.scope||window, null, trans.arg, false);
20554             return;
20555         }
20556         this.fireEvent("load", this, o, trans.arg);
20557         trans.callback.call(trans.scope||window, result, trans.arg, true);
20558     },
20559
20560     // private
20561     handleFailure : function(trans){
20562         this.trans = false;
20563         this.destroyTrans(trans, false);
20564         this.fireEvent("loadexception", this, null, trans.arg);
20565         trans.callback.call(trans.scope||window, null, trans.arg, false);
20566     }
20567 });/*
20568  * Based on:
20569  * Ext JS Library 1.1.1
20570  * Copyright(c) 2006-2007, Ext JS, LLC.
20571  *
20572  * Originally Released Under LGPL - original licence link has changed is not relivant.
20573  *
20574  * Fork - LGPL
20575  * <script type="text/javascript">
20576  */
20577
20578 /**
20579  * @class Roo.data.JsonReader
20580  * @extends Roo.data.DataReader
20581  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20582  * based on mappings in a provided Roo.data.Record constructor.
20583  * 
20584  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20585  * in the reply previously. 
20586  * 
20587  * <p>
20588  * Example code:
20589  * <pre><code>
20590 var RecordDef = Roo.data.Record.create([
20591     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20592     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20593 ]);
20594 var myReader = new Roo.data.JsonReader({
20595     totalProperty: "results",    // The property which contains the total dataset size (optional)
20596     root: "rows",                // The property which contains an Array of row objects
20597     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20598 }, RecordDef);
20599 </code></pre>
20600  * <p>
20601  * This would consume a JSON file like this:
20602  * <pre><code>
20603 { 'results': 2, 'rows': [
20604     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20605     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20606 }
20607 </code></pre>
20608  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20609  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20610  * paged from the remote server.
20611  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20612  * @cfg {String} root name of the property which contains the Array of row objects.
20613  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20614  * @constructor
20615  * Create a new JsonReader
20616  * @param {Object} meta Metadata configuration options
20617  * @param {Object} recordType Either an Array of field definition objects,
20618  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20619  */
20620 Roo.data.JsonReader = function(meta, recordType){
20621     
20622     meta = meta || {};
20623     // set some defaults:
20624     Roo.applyIf(meta, {
20625         totalProperty: 'total',
20626         successProperty : 'success',
20627         root : 'data',
20628         id : 'id'
20629     });
20630     
20631     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20632 };
20633 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20634     
20635     /**
20636      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20637      * Used by Store query builder to append _requestMeta to params.
20638      * 
20639      */
20640     metaFromRemote : false,
20641     /**
20642      * This method is only used by a DataProxy which has retrieved data from a remote server.
20643      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20644      * @return {Object} data A data block which is used by an Roo.data.Store object as
20645      * a cache of Roo.data.Records.
20646      */
20647     read : function(response){
20648         var json = response.responseText;
20649        
20650         var o = /* eval:var:o */ eval("("+json+")");
20651         if(!o) {
20652             throw {message: "JsonReader.read: Json object not found"};
20653         }
20654         
20655         if(o.metaData){
20656             
20657             delete this.ef;
20658             this.metaFromRemote = true;
20659             this.meta = o.metaData;
20660             this.recordType = Roo.data.Record.create(o.metaData.fields);
20661             this.onMetaChange(this.meta, this.recordType, o);
20662         }
20663         return this.readRecords(o);
20664     },
20665
20666     // private function a store will implement
20667     onMetaChange : function(meta, recordType, o){
20668
20669     },
20670
20671     /**
20672          * @ignore
20673          */
20674     simpleAccess: function(obj, subsc) {
20675         return obj[subsc];
20676     },
20677
20678         /**
20679          * @ignore
20680          */
20681     getJsonAccessor: function(){
20682         var re = /[\[\.]/;
20683         return function(expr) {
20684             try {
20685                 return(re.test(expr))
20686                     ? new Function("obj", "return obj." + expr)
20687                     : function(obj){
20688                         return obj[expr];
20689                     };
20690             } catch(e){}
20691             return Roo.emptyFn;
20692         };
20693     }(),
20694
20695     /**
20696      * Create a data block containing Roo.data.Records from an XML document.
20697      * @param {Object} o An object which contains an Array of row objects in the property specified
20698      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20699      * which contains the total size of the dataset.
20700      * @return {Object} data A data block which is used by an Roo.data.Store object as
20701      * a cache of Roo.data.Records.
20702      */
20703     readRecords : function(o){
20704         /**
20705          * After any data loads, the raw JSON data is available for further custom processing.
20706          * @type Object
20707          */
20708         this.jsonData = o;
20709         var s = this.meta, Record = this.recordType,
20710             f = Record.prototype.fields, fi = f.items, fl = f.length;
20711
20712 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20713         if (!this.ef) {
20714             if(s.totalProperty) {
20715                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20716                 }
20717                 if(s.successProperty) {
20718                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20719                 }
20720                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20721                 if (s.id) {
20722                         var g = this.getJsonAccessor(s.id);
20723                         this.getId = function(rec) {
20724                                 var r = g(rec);
20725                                 return (r === undefined || r === "") ? null : r;
20726                         };
20727                 } else {
20728                         this.getId = function(){return null;};
20729                 }
20730             this.ef = [];
20731             for(var jj = 0; jj < fl; jj++){
20732                 f = fi[jj];
20733                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20734                 this.ef[jj] = this.getJsonAccessor(map);
20735             }
20736         }
20737
20738         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20739         if(s.totalProperty){
20740             var vt = parseInt(this.getTotal(o), 10);
20741             if(!isNaN(vt)){
20742                 totalRecords = vt;
20743             }
20744         }
20745         if(s.successProperty){
20746             var vs = this.getSuccess(o);
20747             if(vs === false || vs === 'false'){
20748                 success = false;
20749             }
20750         }
20751         var records = [];
20752             for(var i = 0; i < c; i++){
20753                     var n = root[i];
20754                 var values = {};
20755                 var id = this.getId(n);
20756                 for(var j = 0; j < fl; j++){
20757                     f = fi[j];
20758                 var v = this.ef[j](n);
20759                 if (!f.convert) {
20760                     Roo.log('missing convert for ' + f.name);
20761                     Roo.log(f);
20762                     continue;
20763                 }
20764                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20765                 }
20766                 var record = new Record(values, id);
20767                 record.json = n;
20768                 records[i] = record;
20769             }
20770             return {
20771                 success : success,
20772                 records : records,
20773                 totalRecords : totalRecords
20774             };
20775     }
20776 });/*
20777  * Based on:
20778  * Ext JS Library 1.1.1
20779  * Copyright(c) 2006-2007, Ext JS, LLC.
20780  *
20781  * Originally Released Under LGPL - original licence link has changed is not relivant.
20782  *
20783  * Fork - LGPL
20784  * <script type="text/javascript">
20785  */
20786
20787 /**
20788  * @class Roo.data.XmlReader
20789  * @extends Roo.data.DataReader
20790  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20791  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20792  * <p>
20793  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20794  * header in the HTTP response must be set to "text/xml".</em>
20795  * <p>
20796  * Example code:
20797  * <pre><code>
20798 var RecordDef = Roo.data.Record.create([
20799    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20800    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20801 ]);
20802 var myReader = new Roo.data.XmlReader({
20803    totalRecords: "results", // The element which contains the total dataset size (optional)
20804    record: "row",           // The repeated element which contains row information
20805    id: "id"                 // The element within the row that provides an ID for the record (optional)
20806 }, RecordDef);
20807 </code></pre>
20808  * <p>
20809  * This would consume an XML file like this:
20810  * <pre><code>
20811 &lt;?xml?>
20812 &lt;dataset>
20813  &lt;results>2&lt;/results>
20814  &lt;row>
20815    &lt;id>1&lt;/id>
20816    &lt;name>Bill&lt;/name>
20817    &lt;occupation>Gardener&lt;/occupation>
20818  &lt;/row>
20819  &lt;row>
20820    &lt;id>2&lt;/id>
20821    &lt;name>Ben&lt;/name>
20822    &lt;occupation>Horticulturalist&lt;/occupation>
20823  &lt;/row>
20824 &lt;/dataset>
20825 </code></pre>
20826  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20827  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20828  * paged from the remote server.
20829  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20830  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20831  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20832  * a record identifier value.
20833  * @constructor
20834  * Create a new XmlReader
20835  * @param {Object} meta Metadata configuration options
20836  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20837  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20838  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20839  */
20840 Roo.data.XmlReader = function(meta, recordType){
20841     meta = meta || {};
20842     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20843 };
20844 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20845     /**
20846      * This method is only used by a DataProxy which has retrieved data from a remote server.
20847          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20848          * to contain a method called 'responseXML' that returns an XML document object.
20849      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20850      * a cache of Roo.data.Records.
20851      */
20852     read : function(response){
20853         var doc = response.responseXML;
20854         if(!doc) {
20855             throw {message: "XmlReader.read: XML Document not available"};
20856         }
20857         return this.readRecords(doc);
20858     },
20859
20860     /**
20861      * Create a data block containing Roo.data.Records from an XML document.
20862          * @param {Object} doc A parsed XML document.
20863      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20864      * a cache of Roo.data.Records.
20865      */
20866     readRecords : function(doc){
20867         /**
20868          * After any data loads/reads, the raw XML Document is available for further custom processing.
20869          * @type XMLDocument
20870          */
20871         this.xmlData = doc;
20872         var root = doc.documentElement || doc;
20873         var q = Roo.DomQuery;
20874         var recordType = this.recordType, fields = recordType.prototype.fields;
20875         var sid = this.meta.id;
20876         var totalRecords = 0, success = true;
20877         if(this.meta.totalRecords){
20878             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20879         }
20880         
20881         if(this.meta.success){
20882             var sv = q.selectValue(this.meta.success, root, true);
20883             success = sv !== false && sv !== 'false';
20884         }
20885         var records = [];
20886         var ns = q.select(this.meta.record, root);
20887         for(var i = 0, len = ns.length; i < len; i++) {
20888                 var n = ns[i];
20889                 var values = {};
20890                 var id = sid ? q.selectValue(sid, n) : undefined;
20891                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20892                     var f = fields.items[j];
20893                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20894                     v = f.convert(v);
20895                     values[f.name] = v;
20896                 }
20897                 var record = new recordType(values, id);
20898                 record.node = n;
20899                 records[records.length] = record;
20900             }
20901
20902             return {
20903                 success : success,
20904                 records : records,
20905                 totalRecords : totalRecords || records.length
20906             };
20907     }
20908 });/*
20909  * Based on:
20910  * Ext JS Library 1.1.1
20911  * Copyright(c) 2006-2007, Ext JS, LLC.
20912  *
20913  * Originally Released Under LGPL - original licence link has changed is not relivant.
20914  *
20915  * Fork - LGPL
20916  * <script type="text/javascript">
20917  */
20918
20919 /**
20920  * @class Roo.data.ArrayReader
20921  * @extends Roo.data.DataReader
20922  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20923  * Each element of that Array represents a row of data fields. The
20924  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20925  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20926  * <p>
20927  * Example code:.
20928  * <pre><code>
20929 var RecordDef = Roo.data.Record.create([
20930     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20931     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20932 ]);
20933 var myReader = new Roo.data.ArrayReader({
20934     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20935 }, RecordDef);
20936 </code></pre>
20937  * <p>
20938  * This would consume an Array like this:
20939  * <pre><code>
20940 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20941   </code></pre>
20942  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20943  * @constructor
20944  * Create a new JsonReader
20945  * @param {Object} meta Metadata configuration options.
20946  * @param {Object} recordType Either an Array of field definition objects
20947  * as specified to {@link Roo.data.Record#create},
20948  * or an {@link Roo.data.Record} object
20949  * created using {@link Roo.data.Record#create}.
20950  */
20951 Roo.data.ArrayReader = function(meta, recordType){
20952     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20953 };
20954
20955 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20956     /**
20957      * Create a data block containing Roo.data.Records from an XML document.
20958      * @param {Object} o An Array of row objects which represents the dataset.
20959      * @return {Object} data A data block which is used by an Roo.data.Store object as
20960      * a cache of Roo.data.Records.
20961      */
20962     readRecords : function(o){
20963         var sid = this.meta ? this.meta.id : null;
20964         var recordType = this.recordType, fields = recordType.prototype.fields;
20965         var records = [];
20966         var root = o;
20967             for(var i = 0; i < root.length; i++){
20968                     var n = root[i];
20969                 var values = {};
20970                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20971                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20972                 var f = fields.items[j];
20973                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20974                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20975                 v = f.convert(v);
20976                 values[f.name] = v;
20977             }
20978                 var record = new recordType(values, id);
20979                 record.json = n;
20980                 records[records.length] = record;
20981             }
20982             return {
20983                 records : records,
20984                 totalRecords : records.length
20985             };
20986     }
20987 });/*
20988  * Based on:
20989  * Ext JS Library 1.1.1
20990  * Copyright(c) 2006-2007, Ext JS, LLC.
20991  *
20992  * Originally Released Under LGPL - original licence link has changed is not relivant.
20993  *
20994  * Fork - LGPL
20995  * <script type="text/javascript">
20996  */
20997
20998
20999 /**
21000  * @class Roo.data.Tree
21001  * @extends Roo.util.Observable
21002  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
21003  * in the tree have most standard DOM functionality.
21004  * @constructor
21005  * @param {Node} root (optional) The root node
21006  */
21007 Roo.data.Tree = function(root){
21008    this.nodeHash = {};
21009    /**
21010     * The root node for this tree
21011     * @type Node
21012     */
21013    this.root = null;
21014    if(root){
21015        this.setRootNode(root);
21016    }
21017    this.addEvents({
21018        /**
21019         * @event append
21020         * Fires when a new child node is appended to a node in this tree.
21021         * @param {Tree} tree The owner tree
21022         * @param {Node} parent The parent node
21023         * @param {Node} node The newly appended node
21024         * @param {Number} index The index of the newly appended node
21025         */
21026        "append" : true,
21027        /**
21028         * @event remove
21029         * Fires when a child node is removed from a node in this tree.
21030         * @param {Tree} tree The owner tree
21031         * @param {Node} parent The parent node
21032         * @param {Node} node The child node removed
21033         */
21034        "remove" : true,
21035        /**
21036         * @event move
21037         * Fires when a node is moved to a new location in the tree
21038         * @param {Tree} tree The owner tree
21039         * @param {Node} node The node moved
21040         * @param {Node} oldParent The old parent of this node
21041         * @param {Node} newParent The new parent of this node
21042         * @param {Number} index The index it was moved to
21043         */
21044        "move" : true,
21045        /**
21046         * @event insert
21047         * Fires when a new child node is inserted in a node in this tree.
21048         * @param {Tree} tree The owner tree
21049         * @param {Node} parent The parent node
21050         * @param {Node} node The child node inserted
21051         * @param {Node} refNode The child node the node was inserted before
21052         */
21053        "insert" : true,
21054        /**
21055         * @event beforeappend
21056         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21057         * @param {Tree} tree The owner tree
21058         * @param {Node} parent The parent node
21059         * @param {Node} node The child node to be appended
21060         */
21061        "beforeappend" : true,
21062        /**
21063         * @event beforeremove
21064         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21065         * @param {Tree} tree The owner tree
21066         * @param {Node} parent The parent node
21067         * @param {Node} node The child node to be removed
21068         */
21069        "beforeremove" : true,
21070        /**
21071         * @event beforemove
21072         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21073         * @param {Tree} tree The owner tree
21074         * @param {Node} node The node being moved
21075         * @param {Node} oldParent The parent of the node
21076         * @param {Node} newParent The new parent the node is moving to
21077         * @param {Number} index The index it is being moved to
21078         */
21079        "beforemove" : true,
21080        /**
21081         * @event beforeinsert
21082         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21083         * @param {Tree} tree The owner tree
21084         * @param {Node} parent The parent node
21085         * @param {Node} node The child node to be inserted
21086         * @param {Node} refNode The child node the node is being inserted before
21087         */
21088        "beforeinsert" : true
21089    });
21090
21091     Roo.data.Tree.superclass.constructor.call(this);
21092 };
21093
21094 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21095     pathSeparator: "/",
21096
21097     proxyNodeEvent : function(){
21098         return this.fireEvent.apply(this, arguments);
21099     },
21100
21101     /**
21102      * Returns the root node for this tree.
21103      * @return {Node}
21104      */
21105     getRootNode : function(){
21106         return this.root;
21107     },
21108
21109     /**
21110      * Sets the root node for this tree.
21111      * @param {Node} node
21112      * @return {Node}
21113      */
21114     setRootNode : function(node){
21115         this.root = node;
21116         node.ownerTree = this;
21117         node.isRoot = true;
21118         this.registerNode(node);
21119         return node;
21120     },
21121
21122     /**
21123      * Gets a node in this tree by its id.
21124      * @param {String} id
21125      * @return {Node}
21126      */
21127     getNodeById : function(id){
21128         return this.nodeHash[id];
21129     },
21130
21131     registerNode : function(node){
21132         this.nodeHash[node.id] = node;
21133     },
21134
21135     unregisterNode : function(node){
21136         delete this.nodeHash[node.id];
21137     },
21138
21139     toString : function(){
21140         return "[Tree"+(this.id?" "+this.id:"")+"]";
21141     }
21142 });
21143
21144 /**
21145  * @class Roo.data.Node
21146  * @extends Roo.util.Observable
21147  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21148  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21149  * @constructor
21150  * @param {Object} attributes The attributes/config for the node
21151  */
21152 Roo.data.Node = function(attributes){
21153     /**
21154      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21155      * @type {Object}
21156      */
21157     this.attributes = attributes || {};
21158     this.leaf = this.attributes.leaf;
21159     /**
21160      * The node id. @type String
21161      */
21162     this.id = this.attributes.id;
21163     if(!this.id){
21164         this.id = Roo.id(null, "ynode-");
21165         this.attributes.id = this.id;
21166     }
21167     /**
21168      * All child nodes of this node. @type Array
21169      */
21170     this.childNodes = [];
21171     if(!this.childNodes.indexOf){ // indexOf is a must
21172         this.childNodes.indexOf = function(o){
21173             for(var i = 0, len = this.length; i < len; i++){
21174                 if(this[i] == o) {
21175                     return i;
21176                 }
21177             }
21178             return -1;
21179         };
21180     }
21181     /**
21182      * The parent node for this node. @type Node
21183      */
21184     this.parentNode = null;
21185     /**
21186      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21187      */
21188     this.firstChild = null;
21189     /**
21190      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21191      */
21192     this.lastChild = null;
21193     /**
21194      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21195      */
21196     this.previousSibling = null;
21197     /**
21198      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21199      */
21200     this.nextSibling = null;
21201
21202     this.addEvents({
21203        /**
21204         * @event append
21205         * Fires when a new child node is appended
21206         * @param {Tree} tree The owner tree
21207         * @param {Node} this This node
21208         * @param {Node} node The newly appended node
21209         * @param {Number} index The index of the newly appended node
21210         */
21211        "append" : true,
21212        /**
21213         * @event remove
21214         * Fires when a child node is removed
21215         * @param {Tree} tree The owner tree
21216         * @param {Node} this This node
21217         * @param {Node} node The removed node
21218         */
21219        "remove" : true,
21220        /**
21221         * @event move
21222         * Fires when this node is moved to a new location in the tree
21223         * @param {Tree} tree The owner tree
21224         * @param {Node} this This node
21225         * @param {Node} oldParent The old parent of this node
21226         * @param {Node} newParent The new parent of this node
21227         * @param {Number} index The index it was moved to
21228         */
21229        "move" : true,
21230        /**
21231         * @event insert
21232         * Fires when a new child node is inserted.
21233         * @param {Tree} tree The owner tree
21234         * @param {Node} this This node
21235         * @param {Node} node The child node inserted
21236         * @param {Node} refNode The child node the node was inserted before
21237         */
21238        "insert" : true,
21239        /**
21240         * @event beforeappend
21241         * Fires before a new child is appended, return false to cancel the append.
21242         * @param {Tree} tree The owner tree
21243         * @param {Node} this This node
21244         * @param {Node} node The child node to be appended
21245         */
21246        "beforeappend" : true,
21247        /**
21248         * @event beforeremove
21249         * Fires before a child is removed, return false to cancel the remove.
21250         * @param {Tree} tree The owner tree
21251         * @param {Node} this This node
21252         * @param {Node} node The child node to be removed
21253         */
21254        "beforeremove" : true,
21255        /**
21256         * @event beforemove
21257         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21258         * @param {Tree} tree The owner tree
21259         * @param {Node} this This node
21260         * @param {Node} oldParent The parent of this node
21261         * @param {Node} newParent The new parent this node is moving to
21262         * @param {Number} index The index it is being moved to
21263         */
21264        "beforemove" : true,
21265        /**
21266         * @event beforeinsert
21267         * Fires before a new child is inserted, return false to cancel the insert.
21268         * @param {Tree} tree The owner tree
21269         * @param {Node} this This node
21270         * @param {Node} node The child node to be inserted
21271         * @param {Node} refNode The child node the node is being inserted before
21272         */
21273        "beforeinsert" : true
21274    });
21275     this.listeners = this.attributes.listeners;
21276     Roo.data.Node.superclass.constructor.call(this);
21277 };
21278
21279 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21280     fireEvent : function(evtName){
21281         // first do standard event for this node
21282         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21283             return false;
21284         }
21285         // then bubble it up to the tree if the event wasn't cancelled
21286         var ot = this.getOwnerTree();
21287         if(ot){
21288             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21289                 return false;
21290             }
21291         }
21292         return true;
21293     },
21294
21295     /**
21296      * Returns true if this node is a leaf
21297      * @return {Boolean}
21298      */
21299     isLeaf : function(){
21300         return this.leaf === true;
21301     },
21302
21303     // private
21304     setFirstChild : function(node){
21305         this.firstChild = node;
21306     },
21307
21308     //private
21309     setLastChild : function(node){
21310         this.lastChild = node;
21311     },
21312
21313
21314     /**
21315      * Returns true if this node is the last child of its parent
21316      * @return {Boolean}
21317      */
21318     isLast : function(){
21319        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21320     },
21321
21322     /**
21323      * Returns true if this node is the first child of its parent
21324      * @return {Boolean}
21325      */
21326     isFirst : function(){
21327        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21328     },
21329
21330     hasChildNodes : function(){
21331         return !this.isLeaf() && this.childNodes.length > 0;
21332     },
21333
21334     /**
21335      * Insert node(s) as the last child node of this node.
21336      * @param {Node/Array} node The node or Array of nodes to append
21337      * @return {Node} The appended node if single append, or null if an array was passed
21338      */
21339     appendChild : function(node){
21340         var multi = false;
21341         if(node instanceof Array){
21342             multi = node;
21343         }else if(arguments.length > 1){
21344             multi = arguments;
21345         }
21346         // if passed an array or multiple args do them one by one
21347         if(multi){
21348             for(var i = 0, len = multi.length; i < len; i++) {
21349                 this.appendChild(multi[i]);
21350             }
21351         }else{
21352             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21353                 return false;
21354             }
21355             var index = this.childNodes.length;
21356             var oldParent = node.parentNode;
21357             // it's a move, make sure we move it cleanly
21358             if(oldParent){
21359                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21360                     return false;
21361                 }
21362                 oldParent.removeChild(node);
21363             }
21364             index = this.childNodes.length;
21365             if(index == 0){
21366                 this.setFirstChild(node);
21367             }
21368             this.childNodes.push(node);
21369             node.parentNode = this;
21370             var ps = this.childNodes[index-1];
21371             if(ps){
21372                 node.previousSibling = ps;
21373                 ps.nextSibling = node;
21374             }else{
21375                 node.previousSibling = null;
21376             }
21377             node.nextSibling = null;
21378             this.setLastChild(node);
21379             node.setOwnerTree(this.getOwnerTree());
21380             this.fireEvent("append", this.ownerTree, this, node, index);
21381             if(oldParent){
21382                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21383             }
21384             return node;
21385         }
21386     },
21387
21388     /**
21389      * Removes a child node from this node.
21390      * @param {Node} node The node to remove
21391      * @return {Node} The removed node
21392      */
21393     removeChild : function(node){
21394         var index = this.childNodes.indexOf(node);
21395         if(index == -1){
21396             return false;
21397         }
21398         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21399             return false;
21400         }
21401
21402         // remove it from childNodes collection
21403         this.childNodes.splice(index, 1);
21404
21405         // update siblings
21406         if(node.previousSibling){
21407             node.previousSibling.nextSibling = node.nextSibling;
21408         }
21409         if(node.nextSibling){
21410             node.nextSibling.previousSibling = node.previousSibling;
21411         }
21412
21413         // update child refs
21414         if(this.firstChild == node){
21415             this.setFirstChild(node.nextSibling);
21416         }
21417         if(this.lastChild == node){
21418             this.setLastChild(node.previousSibling);
21419         }
21420
21421         node.setOwnerTree(null);
21422         // clear any references from the node
21423         node.parentNode = null;
21424         node.previousSibling = null;
21425         node.nextSibling = null;
21426         this.fireEvent("remove", this.ownerTree, this, node);
21427         return node;
21428     },
21429
21430     /**
21431      * Inserts the first node before the second node in this nodes childNodes collection.
21432      * @param {Node} node The node to insert
21433      * @param {Node} refNode The node to insert before (if null the node is appended)
21434      * @return {Node} The inserted node
21435      */
21436     insertBefore : function(node, refNode){
21437         if(!refNode){ // like standard Dom, refNode can be null for append
21438             return this.appendChild(node);
21439         }
21440         // nothing to do
21441         if(node == refNode){
21442             return false;
21443         }
21444
21445         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21446             return false;
21447         }
21448         var index = this.childNodes.indexOf(refNode);
21449         var oldParent = node.parentNode;
21450         var refIndex = index;
21451
21452         // when moving internally, indexes will change after remove
21453         if(oldParent == this && this.childNodes.indexOf(node) < index){
21454             refIndex--;
21455         }
21456
21457         // it's a move, make sure we move it cleanly
21458         if(oldParent){
21459             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21460                 return false;
21461             }
21462             oldParent.removeChild(node);
21463         }
21464         if(refIndex == 0){
21465             this.setFirstChild(node);
21466         }
21467         this.childNodes.splice(refIndex, 0, node);
21468         node.parentNode = this;
21469         var ps = this.childNodes[refIndex-1];
21470         if(ps){
21471             node.previousSibling = ps;
21472             ps.nextSibling = node;
21473         }else{
21474             node.previousSibling = null;
21475         }
21476         node.nextSibling = refNode;
21477         refNode.previousSibling = node;
21478         node.setOwnerTree(this.getOwnerTree());
21479         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21480         if(oldParent){
21481             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21482         }
21483         return node;
21484     },
21485
21486     /**
21487      * Returns the child node at the specified index.
21488      * @param {Number} index
21489      * @return {Node}
21490      */
21491     item : function(index){
21492         return this.childNodes[index];
21493     },
21494
21495     /**
21496      * Replaces one child node in this node with another.
21497      * @param {Node} newChild The replacement node
21498      * @param {Node} oldChild The node to replace
21499      * @return {Node} The replaced node
21500      */
21501     replaceChild : function(newChild, oldChild){
21502         this.insertBefore(newChild, oldChild);
21503         this.removeChild(oldChild);
21504         return oldChild;
21505     },
21506
21507     /**
21508      * Returns the index of a child node
21509      * @param {Node} node
21510      * @return {Number} The index of the node or -1 if it was not found
21511      */
21512     indexOf : function(child){
21513         return this.childNodes.indexOf(child);
21514     },
21515
21516     /**
21517      * Returns the tree this node is in.
21518      * @return {Tree}
21519      */
21520     getOwnerTree : function(){
21521         // if it doesn't have one, look for one
21522         if(!this.ownerTree){
21523             var p = this;
21524             while(p){
21525                 if(p.ownerTree){
21526                     this.ownerTree = p.ownerTree;
21527                     break;
21528                 }
21529                 p = p.parentNode;
21530             }
21531         }
21532         return this.ownerTree;
21533     },
21534
21535     /**
21536      * Returns depth of this node (the root node has a depth of 0)
21537      * @return {Number}
21538      */
21539     getDepth : function(){
21540         var depth = 0;
21541         var p = this;
21542         while(p.parentNode){
21543             ++depth;
21544             p = p.parentNode;
21545         }
21546         return depth;
21547     },
21548
21549     // private
21550     setOwnerTree : function(tree){
21551         // if it's move, we need to update everyone
21552         if(tree != this.ownerTree){
21553             if(this.ownerTree){
21554                 this.ownerTree.unregisterNode(this);
21555             }
21556             this.ownerTree = tree;
21557             var cs = this.childNodes;
21558             for(var i = 0, len = cs.length; i < len; i++) {
21559                 cs[i].setOwnerTree(tree);
21560             }
21561             if(tree){
21562                 tree.registerNode(this);
21563             }
21564         }
21565     },
21566
21567     /**
21568      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21569      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21570      * @return {String} The path
21571      */
21572     getPath : function(attr){
21573         attr = attr || "id";
21574         var p = this.parentNode;
21575         var b = [this.attributes[attr]];
21576         while(p){
21577             b.unshift(p.attributes[attr]);
21578             p = p.parentNode;
21579         }
21580         var sep = this.getOwnerTree().pathSeparator;
21581         return sep + b.join(sep);
21582     },
21583
21584     /**
21585      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21586      * function call will be the scope provided or the current node. The arguments to the function
21587      * will be the args provided or the current node. If the function returns false at any point,
21588      * the bubble is stopped.
21589      * @param {Function} fn The function to call
21590      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21591      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21592      */
21593     bubble : function(fn, scope, args){
21594         var p = this;
21595         while(p){
21596             if(fn.call(scope || p, args || p) === false){
21597                 break;
21598             }
21599             p = p.parentNode;
21600         }
21601     },
21602
21603     /**
21604      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21605      * function call will be the scope provided or the current node. The arguments to the function
21606      * will be the args provided or the current node. If the function returns false at any point,
21607      * the cascade is stopped on that branch.
21608      * @param {Function} fn The function to call
21609      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21610      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21611      */
21612     cascade : function(fn, scope, args){
21613         if(fn.call(scope || this, args || this) !== false){
21614             var cs = this.childNodes;
21615             for(var i = 0, len = cs.length; i < len; i++) {
21616                 cs[i].cascade(fn, scope, args);
21617             }
21618         }
21619     },
21620
21621     /**
21622      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21623      * function call will be the scope provided or the current node. The arguments to the function
21624      * will be the args provided or the current node. If the function returns false at any point,
21625      * the iteration stops.
21626      * @param {Function} fn The function to call
21627      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21628      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21629      */
21630     eachChild : function(fn, scope, args){
21631         var cs = this.childNodes;
21632         for(var i = 0, len = cs.length; i < len; i++) {
21633                 if(fn.call(scope || this, args || cs[i]) === false){
21634                     break;
21635                 }
21636         }
21637     },
21638
21639     /**
21640      * Finds the first child that has the attribute with the specified value.
21641      * @param {String} attribute The attribute name
21642      * @param {Mixed} value The value to search for
21643      * @return {Node} The found child or null if none was found
21644      */
21645     findChild : function(attribute, value){
21646         var cs = this.childNodes;
21647         for(var i = 0, len = cs.length; i < len; i++) {
21648                 if(cs[i].attributes[attribute] == value){
21649                     return cs[i];
21650                 }
21651         }
21652         return null;
21653     },
21654
21655     /**
21656      * Finds the first child by a custom function. The child matches if the function passed
21657      * returns true.
21658      * @param {Function} fn
21659      * @param {Object} scope (optional)
21660      * @return {Node} The found child or null if none was found
21661      */
21662     findChildBy : function(fn, scope){
21663         var cs = this.childNodes;
21664         for(var i = 0, len = cs.length; i < len; i++) {
21665                 if(fn.call(scope||cs[i], cs[i]) === true){
21666                     return cs[i];
21667                 }
21668         }
21669         return null;
21670     },
21671
21672     /**
21673      * Sorts this nodes children using the supplied sort function
21674      * @param {Function} fn
21675      * @param {Object} scope (optional)
21676      */
21677     sort : function(fn, scope){
21678         var cs = this.childNodes;
21679         var len = cs.length;
21680         if(len > 0){
21681             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21682             cs.sort(sortFn);
21683             for(var i = 0; i < len; i++){
21684                 var n = cs[i];
21685                 n.previousSibling = cs[i-1];
21686                 n.nextSibling = cs[i+1];
21687                 if(i == 0){
21688                     this.setFirstChild(n);
21689                 }
21690                 if(i == len-1){
21691                     this.setLastChild(n);
21692                 }
21693             }
21694         }
21695     },
21696
21697     /**
21698      * Returns true if this node is an ancestor (at any point) of the passed node.
21699      * @param {Node} node
21700      * @return {Boolean}
21701      */
21702     contains : function(node){
21703         return node.isAncestor(this);
21704     },
21705
21706     /**
21707      * Returns true if the passed node is an ancestor (at any point) of this node.
21708      * @param {Node} node
21709      * @return {Boolean}
21710      */
21711     isAncestor : function(node){
21712         var p = this.parentNode;
21713         while(p){
21714             if(p == node){
21715                 return true;
21716             }
21717             p = p.parentNode;
21718         }
21719         return false;
21720     },
21721
21722     toString : function(){
21723         return "[Node"+(this.id?" "+this.id:"")+"]";
21724     }
21725 });/*
21726  * Based on:
21727  * Ext JS Library 1.1.1
21728  * Copyright(c) 2006-2007, Ext JS, LLC.
21729  *
21730  * Originally Released Under LGPL - original licence link has changed is not relivant.
21731  *
21732  * Fork - LGPL
21733  * <script type="text/javascript">
21734  */
21735  
21736
21737 /**
21738  * @class Roo.ComponentMgr
21739  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21740  * @singleton
21741  */
21742 Roo.ComponentMgr = function(){
21743     var all = new Roo.util.MixedCollection();
21744
21745     return {
21746         /**
21747          * Registers a component.
21748          * @param {Roo.Component} c The component
21749          */
21750         register : function(c){
21751             all.add(c);
21752         },
21753
21754         /**
21755          * Unregisters a component.
21756          * @param {Roo.Component} c The component
21757          */
21758         unregister : function(c){
21759             all.remove(c);
21760         },
21761
21762         /**
21763          * Returns a component by id
21764          * @param {String} id The component id
21765          */
21766         get : function(id){
21767             return all.get(id);
21768         },
21769
21770         /**
21771          * Registers a function that will be called when a specified component is added to ComponentMgr
21772          * @param {String} id The component id
21773          * @param {Funtction} fn The callback function
21774          * @param {Object} scope The scope of the callback
21775          */
21776         onAvailable : function(id, fn, scope){
21777             all.on("add", function(index, o){
21778                 if(o.id == id){
21779                     fn.call(scope || o, o);
21780                     all.un("add", fn, scope);
21781                 }
21782             });
21783         }
21784     };
21785 }();/*
21786  * Based on:
21787  * Ext JS Library 1.1.1
21788  * Copyright(c) 2006-2007, Ext JS, LLC.
21789  *
21790  * Originally Released Under LGPL - original licence link has changed is not relivant.
21791  *
21792  * Fork - LGPL
21793  * <script type="text/javascript">
21794  */
21795  
21796 /**
21797  * @class Roo.Component
21798  * @extends Roo.util.Observable
21799  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21800  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21801  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21802  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21803  * All visual components (widgets) that require rendering into a layout should subclass Component.
21804  * @constructor
21805  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21806  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
21807  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21808  */
21809 Roo.Component = function(config){
21810     config = config || {};
21811     if(config.tagName || config.dom || typeof config == "string"){ // element object
21812         config = {el: config, id: config.id || config};
21813     }
21814     this.initialConfig = config;
21815
21816     Roo.apply(this, config);
21817     this.addEvents({
21818         /**
21819          * @event disable
21820          * Fires after the component is disabled.
21821              * @param {Roo.Component} this
21822              */
21823         disable : true,
21824         /**
21825          * @event enable
21826          * Fires after the component is enabled.
21827              * @param {Roo.Component} this
21828              */
21829         enable : true,
21830         /**
21831          * @event beforeshow
21832          * Fires before the component is shown.  Return false to stop the show.
21833              * @param {Roo.Component} this
21834              */
21835         beforeshow : true,
21836         /**
21837          * @event show
21838          * Fires after the component is shown.
21839              * @param {Roo.Component} this
21840              */
21841         show : true,
21842         /**
21843          * @event beforehide
21844          * Fires before the component is hidden. Return false to stop the hide.
21845              * @param {Roo.Component} this
21846              */
21847         beforehide : true,
21848         /**
21849          * @event hide
21850          * Fires after the component is hidden.
21851              * @param {Roo.Component} this
21852              */
21853         hide : true,
21854         /**
21855          * @event beforerender
21856          * Fires before the component is rendered. Return false to stop the render.
21857              * @param {Roo.Component} this
21858              */
21859         beforerender : true,
21860         /**
21861          * @event render
21862          * Fires after the component is rendered.
21863              * @param {Roo.Component} this
21864              */
21865         render : true,
21866         /**
21867          * @event beforedestroy
21868          * Fires before the component is destroyed. Return false to stop the destroy.
21869              * @param {Roo.Component} this
21870              */
21871         beforedestroy : true,
21872         /**
21873          * @event destroy
21874          * Fires after the component is destroyed.
21875              * @param {Roo.Component} this
21876              */
21877         destroy : true
21878     });
21879     if(!this.id){
21880         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21881     }
21882     Roo.ComponentMgr.register(this);
21883     Roo.Component.superclass.constructor.call(this);
21884     this.initComponent();
21885     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21886         this.render(this.renderTo);
21887         delete this.renderTo;
21888     }
21889 };
21890
21891 /** @private */
21892 Roo.Component.AUTO_ID = 1000;
21893
21894 Roo.extend(Roo.Component, Roo.util.Observable, {
21895     /**
21896      * @scope Roo.Component.prototype
21897      * @type {Boolean}
21898      * true if this component is hidden. Read-only.
21899      */
21900     hidden : false,
21901     /**
21902      * @type {Boolean}
21903      * true if this component is disabled. Read-only.
21904      */
21905     disabled : false,
21906     /**
21907      * @type {Boolean}
21908      * true if this component has been rendered. Read-only.
21909      */
21910     rendered : false,
21911     
21912     /** @cfg {String} disableClass
21913      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21914      */
21915     disabledClass : "x-item-disabled",
21916         /** @cfg {Boolean} allowDomMove
21917          * Whether the component can move the Dom node when rendering (defaults to true).
21918          */
21919     allowDomMove : true,
21920     /** @cfg {String} hideMode
21921      * How this component should hidden. Supported values are
21922      * "visibility" (css visibility), "offsets" (negative offset position) and
21923      * "display" (css display) - defaults to "display".
21924      */
21925     hideMode: 'display',
21926
21927     /** @private */
21928     ctype : "Roo.Component",
21929
21930     /**
21931      * @cfg {String} actionMode 
21932      * which property holds the element that used for  hide() / show() / disable() / enable()
21933      * default is 'el' 
21934      */
21935     actionMode : "el",
21936
21937     /** @private */
21938     getActionEl : function(){
21939         return this[this.actionMode];
21940     },
21941
21942     initComponent : Roo.emptyFn,
21943     /**
21944      * If this is a lazy rendering component, render it to its container element.
21945      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
21946      */
21947     render : function(container, position){
21948         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21949             if(!container && this.el){
21950                 this.el = Roo.get(this.el);
21951                 container = this.el.dom.parentNode;
21952                 this.allowDomMove = false;
21953             }
21954             this.container = Roo.get(container);
21955             this.rendered = true;
21956             if(position !== undefined){
21957                 if(typeof position == 'number'){
21958                     position = this.container.dom.childNodes[position];
21959                 }else{
21960                     position = Roo.getDom(position);
21961                 }
21962             }
21963             this.onRender(this.container, position || null);
21964             if(this.cls){
21965                 this.el.addClass(this.cls);
21966                 delete this.cls;
21967             }
21968             if(this.style){
21969                 this.el.applyStyles(this.style);
21970                 delete this.style;
21971             }
21972             this.fireEvent("render", this);
21973             this.afterRender(this.container);
21974             if(this.hidden){
21975                 this.hide();
21976             }
21977             if(this.disabled){
21978                 this.disable();
21979             }
21980         }
21981         return this;
21982     },
21983
21984     /** @private */
21985     // default function is not really useful
21986     onRender : function(ct, position){
21987         if(this.el){
21988             this.el = Roo.get(this.el);
21989             if(this.allowDomMove !== false){
21990                 ct.dom.insertBefore(this.el.dom, position);
21991             }
21992         }
21993     },
21994
21995     /** @private */
21996     getAutoCreate : function(){
21997         var cfg = typeof this.autoCreate == "object" ?
21998                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21999         if(this.id && !cfg.id){
22000             cfg.id = this.id;
22001         }
22002         return cfg;
22003     },
22004
22005     /** @private */
22006     afterRender : Roo.emptyFn,
22007
22008     /**
22009      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
22010      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
22011      */
22012     destroy : function(){
22013         if(this.fireEvent("beforedestroy", this) !== false){
22014             this.purgeListeners();
22015             this.beforeDestroy();
22016             if(this.rendered){
22017                 this.el.removeAllListeners();
22018                 this.el.remove();
22019                 if(this.actionMode == "container"){
22020                     this.container.remove();
22021                 }
22022             }
22023             this.onDestroy();
22024             Roo.ComponentMgr.unregister(this);
22025             this.fireEvent("destroy", this);
22026         }
22027     },
22028
22029         /** @private */
22030     beforeDestroy : function(){
22031
22032     },
22033
22034         /** @private */
22035         onDestroy : function(){
22036
22037     },
22038
22039     /**
22040      * Returns the underlying {@link Roo.Element}.
22041      * @return {Roo.Element} The element
22042      */
22043     getEl : function(){
22044         return this.el;
22045     },
22046
22047     /**
22048      * Returns the id of this component.
22049      * @return {String}
22050      */
22051     getId : function(){
22052         return this.id;
22053     },
22054
22055     /**
22056      * Try to focus this component.
22057      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22058      * @return {Roo.Component} this
22059      */
22060     focus : function(selectText){
22061         if(this.rendered){
22062             this.el.focus();
22063             if(selectText === true){
22064                 this.el.dom.select();
22065             }
22066         }
22067         return this;
22068     },
22069
22070     /** @private */
22071     blur : function(){
22072         if(this.rendered){
22073             this.el.blur();
22074         }
22075         return this;
22076     },
22077
22078     /**
22079      * Disable this component.
22080      * @return {Roo.Component} this
22081      */
22082     disable : function(){
22083         if(this.rendered){
22084             this.onDisable();
22085         }
22086         this.disabled = true;
22087         this.fireEvent("disable", this);
22088         return this;
22089     },
22090
22091         // private
22092     onDisable : function(){
22093         this.getActionEl().addClass(this.disabledClass);
22094         this.el.dom.disabled = true;
22095     },
22096
22097     /**
22098      * Enable this component.
22099      * @return {Roo.Component} this
22100      */
22101     enable : function(){
22102         if(this.rendered){
22103             this.onEnable();
22104         }
22105         this.disabled = false;
22106         this.fireEvent("enable", this);
22107         return this;
22108     },
22109
22110         // private
22111     onEnable : function(){
22112         this.getActionEl().removeClass(this.disabledClass);
22113         this.el.dom.disabled = false;
22114     },
22115
22116     /**
22117      * Convenience function for setting disabled/enabled by boolean.
22118      * @param {Boolean} disabled
22119      */
22120     setDisabled : function(disabled){
22121         this[disabled ? "disable" : "enable"]();
22122     },
22123
22124     /**
22125      * Show this component.
22126      * @return {Roo.Component} this
22127      */
22128     show: function(){
22129         if(this.fireEvent("beforeshow", this) !== false){
22130             this.hidden = false;
22131             if(this.rendered){
22132                 this.onShow();
22133             }
22134             this.fireEvent("show", this);
22135         }
22136         return this;
22137     },
22138
22139     // private
22140     onShow : function(){
22141         var ae = this.getActionEl();
22142         if(this.hideMode == 'visibility'){
22143             ae.dom.style.visibility = "visible";
22144         }else if(this.hideMode == 'offsets'){
22145             ae.removeClass('x-hidden');
22146         }else{
22147             ae.dom.style.display = "";
22148         }
22149     },
22150
22151     /**
22152      * Hide this component.
22153      * @return {Roo.Component} this
22154      */
22155     hide: function(){
22156         if(this.fireEvent("beforehide", this) !== false){
22157             this.hidden = true;
22158             if(this.rendered){
22159                 this.onHide();
22160             }
22161             this.fireEvent("hide", this);
22162         }
22163         return this;
22164     },
22165
22166     // private
22167     onHide : function(){
22168         var ae = this.getActionEl();
22169         if(this.hideMode == 'visibility'){
22170             ae.dom.style.visibility = "hidden";
22171         }else if(this.hideMode == 'offsets'){
22172             ae.addClass('x-hidden');
22173         }else{
22174             ae.dom.style.display = "none";
22175         }
22176     },
22177
22178     /**
22179      * Convenience function to hide or show this component by boolean.
22180      * @param {Boolean} visible True to show, false to hide
22181      * @return {Roo.Component} this
22182      */
22183     setVisible: function(visible){
22184         if(visible) {
22185             this.show();
22186         }else{
22187             this.hide();
22188         }
22189         return this;
22190     },
22191
22192     /**
22193      * Returns true if this component is visible.
22194      */
22195     isVisible : function(){
22196         return this.getActionEl().isVisible();
22197     },
22198
22199     cloneConfig : function(overrides){
22200         overrides = overrides || {};
22201         var id = overrides.id || Roo.id();
22202         var cfg = Roo.applyIf(overrides, this.initialConfig);
22203         cfg.id = id; // prevent dup id
22204         return new this.constructor(cfg);
22205     }
22206 });/*
22207  * Based on:
22208  * Ext JS Library 1.1.1
22209  * Copyright(c) 2006-2007, Ext JS, LLC.
22210  *
22211  * Originally Released Under LGPL - original licence link has changed is not relivant.
22212  *
22213  * Fork - LGPL
22214  * <script type="text/javascript">
22215  */
22216  (function(){ 
22217 /**
22218  * @class Roo.Layer
22219  * @extends Roo.Element
22220  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22221  * automatic maintaining of shadow/shim positions.
22222  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22223  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22224  * you can pass a string with a CSS class name. False turns off the shadow.
22225  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22226  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22227  * @cfg {String} cls CSS class to add to the element
22228  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22229  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22230  * @constructor
22231  * @param {Object} config An object with config options.
22232  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22233  */
22234
22235 Roo.Layer = function(config, existingEl){
22236     config = config || {};
22237     var dh = Roo.DomHelper;
22238     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22239     if(existingEl){
22240         this.dom = Roo.getDom(existingEl);
22241     }
22242     if(!this.dom){
22243         var o = config.dh || {tag: "div", cls: "x-layer"};
22244         this.dom = dh.append(pel, o);
22245     }
22246     if(config.cls){
22247         this.addClass(config.cls);
22248     }
22249     this.constrain = config.constrain !== false;
22250     this.visibilityMode = Roo.Element.VISIBILITY;
22251     if(config.id){
22252         this.id = this.dom.id = config.id;
22253     }else{
22254         this.id = Roo.id(this.dom);
22255     }
22256     this.zindex = config.zindex || this.getZIndex();
22257     this.position("absolute", this.zindex);
22258     if(config.shadow){
22259         this.shadowOffset = config.shadowOffset || 4;
22260         this.shadow = new Roo.Shadow({
22261             offset : this.shadowOffset,
22262             mode : config.shadow
22263         });
22264     }else{
22265         this.shadowOffset = 0;
22266     }
22267     this.useShim = config.shim !== false && Roo.useShims;
22268     this.useDisplay = config.useDisplay;
22269     this.hide();
22270 };
22271
22272 var supr = Roo.Element.prototype;
22273
22274 // shims are shared among layer to keep from having 100 iframes
22275 var shims = [];
22276
22277 Roo.extend(Roo.Layer, Roo.Element, {
22278
22279     getZIndex : function(){
22280         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22281     },
22282
22283     getShim : function(){
22284         if(!this.useShim){
22285             return null;
22286         }
22287         if(this.shim){
22288             return this.shim;
22289         }
22290         var shim = shims.shift();
22291         if(!shim){
22292             shim = this.createShim();
22293             shim.enableDisplayMode('block');
22294             shim.dom.style.display = 'none';
22295             shim.dom.style.visibility = 'visible';
22296         }
22297         var pn = this.dom.parentNode;
22298         if(shim.dom.parentNode != pn){
22299             pn.insertBefore(shim.dom, this.dom);
22300         }
22301         shim.setStyle('z-index', this.getZIndex()-2);
22302         this.shim = shim;
22303         return shim;
22304     },
22305
22306     hideShim : function(){
22307         if(this.shim){
22308             this.shim.setDisplayed(false);
22309             shims.push(this.shim);
22310             delete this.shim;
22311         }
22312     },
22313
22314     disableShadow : function(){
22315         if(this.shadow){
22316             this.shadowDisabled = true;
22317             this.shadow.hide();
22318             this.lastShadowOffset = this.shadowOffset;
22319             this.shadowOffset = 0;
22320         }
22321     },
22322
22323     enableShadow : function(show){
22324         if(this.shadow){
22325             this.shadowDisabled = false;
22326             this.shadowOffset = this.lastShadowOffset;
22327             delete this.lastShadowOffset;
22328             if(show){
22329                 this.sync(true);
22330             }
22331         }
22332     },
22333
22334     // private
22335     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22336     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22337     sync : function(doShow){
22338         var sw = this.shadow;
22339         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22340             var sh = this.getShim();
22341
22342             var w = this.getWidth(),
22343                 h = this.getHeight();
22344
22345             var l = this.getLeft(true),
22346                 t = this.getTop(true);
22347
22348             if(sw && !this.shadowDisabled){
22349                 if(doShow && !sw.isVisible()){
22350                     sw.show(this);
22351                 }else{
22352                     sw.realign(l, t, w, h);
22353                 }
22354                 if(sh){
22355                     if(doShow){
22356                        sh.show();
22357                     }
22358                     // fit the shim behind the shadow, so it is shimmed too
22359                     var a = sw.adjusts, s = sh.dom.style;
22360                     s.left = (Math.min(l, l+a.l))+"px";
22361                     s.top = (Math.min(t, t+a.t))+"px";
22362                     s.width = (w+a.w)+"px";
22363                     s.height = (h+a.h)+"px";
22364                 }
22365             }else if(sh){
22366                 if(doShow){
22367                    sh.show();
22368                 }
22369                 sh.setSize(w, h);
22370                 sh.setLeftTop(l, t);
22371             }
22372             
22373         }
22374     },
22375
22376     // private
22377     destroy : function(){
22378         this.hideShim();
22379         if(this.shadow){
22380             this.shadow.hide();
22381         }
22382         this.removeAllListeners();
22383         var pn = this.dom.parentNode;
22384         if(pn){
22385             pn.removeChild(this.dom);
22386         }
22387         Roo.Element.uncache(this.id);
22388     },
22389
22390     remove : function(){
22391         this.destroy();
22392     },
22393
22394     // private
22395     beginUpdate : function(){
22396         this.updating = true;
22397     },
22398
22399     // private
22400     endUpdate : function(){
22401         this.updating = false;
22402         this.sync(true);
22403     },
22404
22405     // private
22406     hideUnders : function(negOffset){
22407         if(this.shadow){
22408             this.shadow.hide();
22409         }
22410         this.hideShim();
22411     },
22412
22413     // private
22414     constrainXY : function(){
22415         if(this.constrain){
22416             var vw = Roo.lib.Dom.getViewWidth(),
22417                 vh = Roo.lib.Dom.getViewHeight();
22418             var s = Roo.get(document).getScroll();
22419
22420             var xy = this.getXY();
22421             var x = xy[0], y = xy[1];   
22422             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22423             // only move it if it needs it
22424             var moved = false;
22425             // first validate right/bottom
22426             if((x + w) > vw+s.left){
22427                 x = vw - w - this.shadowOffset;
22428                 moved = true;
22429             }
22430             if((y + h) > vh+s.top){
22431                 y = vh - h - this.shadowOffset;
22432                 moved = true;
22433             }
22434             // then make sure top/left isn't negative
22435             if(x < s.left){
22436                 x = s.left;
22437                 moved = true;
22438             }
22439             if(y < s.top){
22440                 y = s.top;
22441                 moved = true;
22442             }
22443             if(moved){
22444                 if(this.avoidY){
22445                     var ay = this.avoidY;
22446                     if(y <= ay && (y+h) >= ay){
22447                         y = ay-h-5;   
22448                     }
22449                 }
22450                 xy = [x, y];
22451                 this.storeXY(xy);
22452                 supr.setXY.call(this, xy);
22453                 this.sync();
22454             }
22455         }
22456     },
22457
22458     isVisible : function(){
22459         return this.visible;    
22460     },
22461
22462     // private
22463     showAction : function(){
22464         this.visible = true; // track visibility to prevent getStyle calls
22465         if(this.useDisplay === true){
22466             this.setDisplayed("");
22467         }else if(this.lastXY){
22468             supr.setXY.call(this, this.lastXY);
22469         }else if(this.lastLT){
22470             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22471         }
22472     },
22473
22474     // private
22475     hideAction : function(){
22476         this.visible = false;
22477         if(this.useDisplay === true){
22478             this.setDisplayed(false);
22479         }else{
22480             this.setLeftTop(-10000,-10000);
22481         }
22482     },
22483
22484     // overridden Element method
22485     setVisible : function(v, a, d, c, e){
22486         if(v){
22487             this.showAction();
22488         }
22489         if(a && v){
22490             var cb = function(){
22491                 this.sync(true);
22492                 if(c){
22493                     c();
22494                 }
22495             }.createDelegate(this);
22496             supr.setVisible.call(this, true, true, d, cb, e);
22497         }else{
22498             if(!v){
22499                 this.hideUnders(true);
22500             }
22501             var cb = c;
22502             if(a){
22503                 cb = function(){
22504                     this.hideAction();
22505                     if(c){
22506                         c();
22507                     }
22508                 }.createDelegate(this);
22509             }
22510             supr.setVisible.call(this, v, a, d, cb, e);
22511             if(v){
22512                 this.sync(true);
22513             }else if(!a){
22514                 this.hideAction();
22515             }
22516         }
22517     },
22518
22519     storeXY : function(xy){
22520         delete this.lastLT;
22521         this.lastXY = xy;
22522     },
22523
22524     storeLeftTop : function(left, top){
22525         delete this.lastXY;
22526         this.lastLT = [left, top];
22527     },
22528
22529     // private
22530     beforeFx : function(){
22531         this.beforeAction();
22532         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22533     },
22534
22535     // private
22536     afterFx : function(){
22537         Roo.Layer.superclass.afterFx.apply(this, arguments);
22538         this.sync(this.isVisible());
22539     },
22540
22541     // private
22542     beforeAction : function(){
22543         if(!this.updating && this.shadow){
22544             this.shadow.hide();
22545         }
22546     },
22547
22548     // overridden Element method
22549     setLeft : function(left){
22550         this.storeLeftTop(left, this.getTop(true));
22551         supr.setLeft.apply(this, arguments);
22552         this.sync();
22553     },
22554
22555     setTop : function(top){
22556         this.storeLeftTop(this.getLeft(true), top);
22557         supr.setTop.apply(this, arguments);
22558         this.sync();
22559     },
22560
22561     setLeftTop : function(left, top){
22562         this.storeLeftTop(left, top);
22563         supr.setLeftTop.apply(this, arguments);
22564         this.sync();
22565     },
22566
22567     setXY : function(xy, a, d, c, e){
22568         this.fixDisplay();
22569         this.beforeAction();
22570         this.storeXY(xy);
22571         var cb = this.createCB(c);
22572         supr.setXY.call(this, xy, a, d, cb, e);
22573         if(!a){
22574             cb();
22575         }
22576     },
22577
22578     // private
22579     createCB : function(c){
22580         var el = this;
22581         return function(){
22582             el.constrainXY();
22583             el.sync(true);
22584             if(c){
22585                 c();
22586             }
22587         };
22588     },
22589
22590     // overridden Element method
22591     setX : function(x, a, d, c, e){
22592         this.setXY([x, this.getY()], a, d, c, e);
22593     },
22594
22595     // overridden Element method
22596     setY : function(y, a, d, c, e){
22597         this.setXY([this.getX(), y], a, d, c, e);
22598     },
22599
22600     // overridden Element method
22601     setSize : function(w, h, a, d, c, e){
22602         this.beforeAction();
22603         var cb = this.createCB(c);
22604         supr.setSize.call(this, w, h, a, d, cb, e);
22605         if(!a){
22606             cb();
22607         }
22608     },
22609
22610     // overridden Element method
22611     setWidth : function(w, a, d, c, e){
22612         this.beforeAction();
22613         var cb = this.createCB(c);
22614         supr.setWidth.call(this, w, a, d, cb, e);
22615         if(!a){
22616             cb();
22617         }
22618     },
22619
22620     // overridden Element method
22621     setHeight : function(h, a, d, c, e){
22622         this.beforeAction();
22623         var cb = this.createCB(c);
22624         supr.setHeight.call(this, h, a, d, cb, e);
22625         if(!a){
22626             cb();
22627         }
22628     },
22629
22630     // overridden Element method
22631     setBounds : function(x, y, w, h, a, d, c, e){
22632         this.beforeAction();
22633         var cb = this.createCB(c);
22634         if(!a){
22635             this.storeXY([x, y]);
22636             supr.setXY.call(this, [x, y]);
22637             supr.setSize.call(this, w, h, a, d, cb, e);
22638             cb();
22639         }else{
22640             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22641         }
22642         return this;
22643     },
22644     
22645     /**
22646      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22647      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22648      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22649      * @param {Number} zindex The new z-index to set
22650      * @return {this} The Layer
22651      */
22652     setZIndex : function(zindex){
22653         this.zindex = zindex;
22654         this.setStyle("z-index", zindex + 2);
22655         if(this.shadow){
22656             this.shadow.setZIndex(zindex + 1);
22657         }
22658         if(this.shim){
22659             this.shim.setStyle("z-index", zindex);
22660         }
22661     }
22662 });
22663 })();/*
22664  * Based on:
22665  * Ext JS Library 1.1.1
22666  * Copyright(c) 2006-2007, Ext JS, LLC.
22667  *
22668  * Originally Released Under LGPL - original licence link has changed is not relivant.
22669  *
22670  * Fork - LGPL
22671  * <script type="text/javascript">
22672  */
22673
22674
22675 /**
22676  * @class Roo.Shadow
22677  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22678  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22679  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22680  * @constructor
22681  * Create a new Shadow
22682  * @param {Object} config The config object
22683  */
22684 Roo.Shadow = function(config){
22685     Roo.apply(this, config);
22686     if(typeof this.mode != "string"){
22687         this.mode = this.defaultMode;
22688     }
22689     var o = this.offset, a = {h: 0};
22690     var rad = Math.floor(this.offset/2);
22691     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22692         case "drop":
22693             a.w = 0;
22694             a.l = a.t = o;
22695             a.t -= 1;
22696             if(Roo.isIE){
22697                 a.l -= this.offset + rad;
22698                 a.t -= this.offset + rad;
22699                 a.w -= rad;
22700                 a.h -= rad;
22701                 a.t += 1;
22702             }
22703         break;
22704         case "sides":
22705             a.w = (o*2);
22706             a.l = -o;
22707             a.t = o-1;
22708             if(Roo.isIE){
22709                 a.l -= (this.offset - rad);
22710                 a.t -= this.offset + rad;
22711                 a.l += 1;
22712                 a.w -= (this.offset - rad)*2;
22713                 a.w -= rad + 1;
22714                 a.h -= 1;
22715             }
22716         break;
22717         case "frame":
22718             a.w = a.h = (o*2);
22719             a.l = a.t = -o;
22720             a.t += 1;
22721             a.h -= 2;
22722             if(Roo.isIE){
22723                 a.l -= (this.offset - rad);
22724                 a.t -= (this.offset - rad);
22725                 a.l += 1;
22726                 a.w -= (this.offset + rad + 1);
22727                 a.h -= (this.offset + rad);
22728                 a.h += 1;
22729             }
22730         break;
22731     };
22732
22733     this.adjusts = a;
22734 };
22735
22736 Roo.Shadow.prototype = {
22737     /**
22738      * @cfg {String} mode
22739      * The shadow display mode.  Supports the following options:<br />
22740      * sides: Shadow displays on both sides and bottom only<br />
22741      * frame: Shadow displays equally on all four sides<br />
22742      * drop: Traditional bottom-right drop shadow (default)
22743      */
22744     /**
22745      * @cfg {String} offset
22746      * The number of pixels to offset the shadow from the element (defaults to 4)
22747      */
22748     offset: 4,
22749
22750     // private
22751     defaultMode: "drop",
22752
22753     /**
22754      * Displays the shadow under the target element
22755      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22756      */
22757     show : function(target){
22758         target = Roo.get(target);
22759         if(!this.el){
22760             this.el = Roo.Shadow.Pool.pull();
22761             if(this.el.dom.nextSibling != target.dom){
22762                 this.el.insertBefore(target);
22763             }
22764         }
22765         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22766         if(Roo.isIE){
22767             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22768         }
22769         this.realign(
22770             target.getLeft(true),
22771             target.getTop(true),
22772             target.getWidth(),
22773             target.getHeight()
22774         );
22775         this.el.dom.style.display = "block";
22776     },
22777
22778     /**
22779      * Returns true if the shadow is visible, else false
22780      */
22781     isVisible : function(){
22782         return this.el ? true : false;  
22783     },
22784
22785     /**
22786      * Direct alignment when values are already available. Show must be called at least once before
22787      * calling this method to ensure it is initialized.
22788      * @param {Number} left The target element left position
22789      * @param {Number} top The target element top position
22790      * @param {Number} width The target element width
22791      * @param {Number} height The target element height
22792      */
22793     realign : function(l, t, w, h){
22794         if(!this.el){
22795             return;
22796         }
22797         var a = this.adjusts, d = this.el.dom, s = d.style;
22798         var iea = 0;
22799         s.left = (l+a.l)+"px";
22800         s.top = (t+a.t)+"px";
22801         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22802  
22803         if(s.width != sws || s.height != shs){
22804             s.width = sws;
22805             s.height = shs;
22806             if(!Roo.isIE){
22807                 var cn = d.childNodes;
22808                 var sww = Math.max(0, (sw-12))+"px";
22809                 cn[0].childNodes[1].style.width = sww;
22810                 cn[1].childNodes[1].style.width = sww;
22811                 cn[2].childNodes[1].style.width = sww;
22812                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22813             }
22814         }
22815     },
22816
22817     /**
22818      * Hides this shadow
22819      */
22820     hide : function(){
22821         if(this.el){
22822             this.el.dom.style.display = "none";
22823             Roo.Shadow.Pool.push(this.el);
22824             delete this.el;
22825         }
22826     },
22827
22828     /**
22829      * Adjust the z-index of this shadow
22830      * @param {Number} zindex The new z-index
22831      */
22832     setZIndex : function(z){
22833         this.zIndex = z;
22834         if(this.el){
22835             this.el.setStyle("z-index", z);
22836         }
22837     }
22838 };
22839
22840 // Private utility class that manages the internal Shadow cache
22841 Roo.Shadow.Pool = function(){
22842     var p = [];
22843     var markup = Roo.isIE ?
22844                  '<div class="x-ie-shadow"></div>' :
22845                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
22846     return {
22847         pull : function(){
22848             var sh = p.shift();
22849             if(!sh){
22850                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22851                 sh.autoBoxAdjust = false;
22852             }
22853             return sh;
22854         },
22855
22856         push : function(sh){
22857             p.push(sh);
22858         }
22859     };
22860 }();/*
22861  * Based on:
22862  * Ext JS Library 1.1.1
22863  * Copyright(c) 2006-2007, Ext JS, LLC.
22864  *
22865  * Originally Released Under LGPL - original licence link has changed is not relivant.
22866  *
22867  * Fork - LGPL
22868  * <script type="text/javascript">
22869  */
22870
22871 /**
22872  * @class Roo.BoxComponent
22873  * @extends Roo.Component
22874  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22875  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22876  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22877  * layout containers.
22878  * @constructor
22879  * @param {Roo.Element/String/Object} config The configuration options.
22880  */
22881 Roo.BoxComponent = function(config){
22882     Roo.Component.call(this, config);
22883     this.addEvents({
22884         /**
22885          * @event resize
22886          * Fires after the component is resized.
22887              * @param {Roo.Component} this
22888              * @param {Number} adjWidth The box-adjusted width that was set
22889              * @param {Number} adjHeight The box-adjusted height that was set
22890              * @param {Number} rawWidth The width that was originally specified
22891              * @param {Number} rawHeight The height that was originally specified
22892              */
22893         resize : true,
22894         /**
22895          * @event move
22896          * Fires after the component is moved.
22897              * @param {Roo.Component} this
22898              * @param {Number} x The new x position
22899              * @param {Number} y The new y position
22900              */
22901         move : true
22902     });
22903 };
22904
22905 Roo.extend(Roo.BoxComponent, Roo.Component, {
22906     // private, set in afterRender to signify that the component has been rendered
22907     boxReady : false,
22908     // private, used to defer height settings to subclasses
22909     deferHeight: false,
22910     /** @cfg {Number} width
22911      * width (optional) size of component
22912      */
22913      /** @cfg {Number} height
22914      * height (optional) size of component
22915      */
22916      
22917     /**
22918      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22919      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22920      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22921      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22922      * @return {Roo.BoxComponent} this
22923      */
22924     setSize : function(w, h){
22925         // support for standard size objects
22926         if(typeof w == 'object'){
22927             h = w.height;
22928             w = w.width;
22929         }
22930         // not rendered
22931         if(!this.boxReady){
22932             this.width = w;
22933             this.height = h;
22934             return this;
22935         }
22936
22937         // prevent recalcs when not needed
22938         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22939             return this;
22940         }
22941         this.lastSize = {width: w, height: h};
22942
22943         var adj = this.adjustSize(w, h);
22944         var aw = adj.width, ah = adj.height;
22945         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22946             var rz = this.getResizeEl();
22947             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22948                 rz.setSize(aw, ah);
22949             }else if(!this.deferHeight && ah !== undefined){
22950                 rz.setHeight(ah);
22951             }else if(aw !== undefined){
22952                 rz.setWidth(aw);
22953             }
22954             this.onResize(aw, ah, w, h);
22955             this.fireEvent('resize', this, aw, ah, w, h);
22956         }
22957         return this;
22958     },
22959
22960     /**
22961      * Gets the current size of the component's underlying element.
22962      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22963      */
22964     getSize : function(){
22965         return this.el.getSize();
22966     },
22967
22968     /**
22969      * Gets the current XY position of the component's underlying element.
22970      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22971      * @return {Array} The XY position of the element (e.g., [100, 200])
22972      */
22973     getPosition : function(local){
22974         if(local === true){
22975             return [this.el.getLeft(true), this.el.getTop(true)];
22976         }
22977         return this.xy || this.el.getXY();
22978     },
22979
22980     /**
22981      * Gets the current box measurements of the component's underlying element.
22982      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22983      * @returns {Object} box An object in the format {x, y, width, height}
22984      */
22985     getBox : function(local){
22986         var s = this.el.getSize();
22987         if(local){
22988             s.x = this.el.getLeft(true);
22989             s.y = this.el.getTop(true);
22990         }else{
22991             var xy = this.xy || this.el.getXY();
22992             s.x = xy[0];
22993             s.y = xy[1];
22994         }
22995         return s;
22996     },
22997
22998     /**
22999      * Sets the current box measurements of the component's underlying element.
23000      * @param {Object} box An object in the format {x, y, width, height}
23001      * @returns {Roo.BoxComponent} this
23002      */
23003     updateBox : function(box){
23004         this.setSize(box.width, box.height);
23005         this.setPagePosition(box.x, box.y);
23006         return this;
23007     },
23008
23009     // protected
23010     getResizeEl : function(){
23011         return this.resizeEl || this.el;
23012     },
23013
23014     // protected
23015     getPositionEl : function(){
23016         return this.positionEl || this.el;
23017     },
23018
23019     /**
23020      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
23021      * This method fires the move event.
23022      * @param {Number} left The new left
23023      * @param {Number} top The new top
23024      * @returns {Roo.BoxComponent} this
23025      */
23026     setPosition : function(x, y){
23027         this.x = x;
23028         this.y = y;
23029         if(!this.boxReady){
23030             return this;
23031         }
23032         var adj = this.adjustPosition(x, y);
23033         var ax = adj.x, ay = adj.y;
23034
23035         var el = this.getPositionEl();
23036         if(ax !== undefined || ay !== undefined){
23037             if(ax !== undefined && ay !== undefined){
23038                 el.setLeftTop(ax, ay);
23039             }else if(ax !== undefined){
23040                 el.setLeft(ax);
23041             }else if(ay !== undefined){
23042                 el.setTop(ay);
23043             }
23044             this.onPosition(ax, ay);
23045             this.fireEvent('move', this, ax, ay);
23046         }
23047         return this;
23048     },
23049
23050     /**
23051      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23052      * This method fires the move event.
23053      * @param {Number} x The new x position
23054      * @param {Number} y The new y position
23055      * @returns {Roo.BoxComponent} this
23056      */
23057     setPagePosition : function(x, y){
23058         this.pageX = x;
23059         this.pageY = y;
23060         if(!this.boxReady){
23061             return;
23062         }
23063         if(x === undefined || y === undefined){ // cannot translate undefined points
23064             return;
23065         }
23066         var p = this.el.translatePoints(x, y);
23067         this.setPosition(p.left, p.top);
23068         return this;
23069     },
23070
23071     // private
23072     onRender : function(ct, position){
23073         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23074         if(this.resizeEl){
23075             this.resizeEl = Roo.get(this.resizeEl);
23076         }
23077         if(this.positionEl){
23078             this.positionEl = Roo.get(this.positionEl);
23079         }
23080     },
23081
23082     // private
23083     afterRender : function(){
23084         Roo.BoxComponent.superclass.afterRender.call(this);
23085         this.boxReady = true;
23086         this.setSize(this.width, this.height);
23087         if(this.x || this.y){
23088             this.setPosition(this.x, this.y);
23089         }
23090         if(this.pageX || this.pageY){
23091             this.setPagePosition(this.pageX, this.pageY);
23092         }
23093     },
23094
23095     /**
23096      * Force the component's size to recalculate based on the underlying element's current height and width.
23097      * @returns {Roo.BoxComponent} this
23098      */
23099     syncSize : function(){
23100         delete this.lastSize;
23101         this.setSize(this.el.getWidth(), this.el.getHeight());
23102         return this;
23103     },
23104
23105     /**
23106      * Called after the component is resized, this method is empty by default but can be implemented by any
23107      * subclass that needs to perform custom logic after a resize occurs.
23108      * @param {Number} adjWidth The box-adjusted width that was set
23109      * @param {Number} adjHeight The box-adjusted height that was set
23110      * @param {Number} rawWidth The width that was originally specified
23111      * @param {Number} rawHeight The height that was originally specified
23112      */
23113     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23114
23115     },
23116
23117     /**
23118      * Called after the component is moved, this method is empty by default but can be implemented by any
23119      * subclass that needs to perform custom logic after a move occurs.
23120      * @param {Number} x The new x position
23121      * @param {Number} y The new y position
23122      */
23123     onPosition : function(x, y){
23124
23125     },
23126
23127     // private
23128     adjustSize : function(w, h){
23129         if(this.autoWidth){
23130             w = 'auto';
23131         }
23132         if(this.autoHeight){
23133             h = 'auto';
23134         }
23135         return {width : w, height: h};
23136     },
23137
23138     // private
23139     adjustPosition : function(x, y){
23140         return {x : x, y: y};
23141     }
23142 });/*
23143  * Based on:
23144  * Ext JS Library 1.1.1
23145  * Copyright(c) 2006-2007, Ext JS, LLC.
23146  *
23147  * Originally Released Under LGPL - original licence link has changed is not relivant.
23148  *
23149  * Fork - LGPL
23150  * <script type="text/javascript">
23151  */
23152
23153
23154 /**
23155  * @class Roo.SplitBar
23156  * @extends Roo.util.Observable
23157  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23158  * <br><br>
23159  * Usage:
23160  * <pre><code>
23161 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23162                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23163 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23164 split.minSize = 100;
23165 split.maxSize = 600;
23166 split.animate = true;
23167 split.on('moved', splitterMoved);
23168 </code></pre>
23169  * @constructor
23170  * Create a new SplitBar
23171  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23172  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23173  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23174  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23175                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23176                         position of the SplitBar).
23177  */
23178 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23179     
23180     /** @private */
23181     this.el = Roo.get(dragElement, true);
23182     this.el.dom.unselectable = "on";
23183     /** @private */
23184     this.resizingEl = Roo.get(resizingElement, true);
23185
23186     /**
23187      * @private
23188      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23189      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23190      * @type Number
23191      */
23192     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23193     
23194     /**
23195      * The minimum size of the resizing element. (Defaults to 0)
23196      * @type Number
23197      */
23198     this.minSize = 0;
23199     
23200     /**
23201      * The maximum size of the resizing element. (Defaults to 2000)
23202      * @type Number
23203      */
23204     this.maxSize = 2000;
23205     
23206     /**
23207      * Whether to animate the transition to the new size
23208      * @type Boolean
23209      */
23210     this.animate = false;
23211     
23212     /**
23213      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23214      * @type Boolean
23215      */
23216     this.useShim = false;
23217     
23218     /** @private */
23219     this.shim = null;
23220     
23221     if(!existingProxy){
23222         /** @private */
23223         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23224     }else{
23225         this.proxy = Roo.get(existingProxy).dom;
23226     }
23227     /** @private */
23228     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23229     
23230     /** @private */
23231     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23232     
23233     /** @private */
23234     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23235     
23236     /** @private */
23237     this.dragSpecs = {};
23238     
23239     /**
23240      * @private The adapter to use to positon and resize elements
23241      */
23242     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23243     this.adapter.init(this);
23244     
23245     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23246         /** @private */
23247         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23248         this.el.addClass("x-splitbar-h");
23249     }else{
23250         /** @private */
23251         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23252         this.el.addClass("x-splitbar-v");
23253     }
23254     
23255     this.addEvents({
23256         /**
23257          * @event resize
23258          * Fires when the splitter is moved (alias for {@link #event-moved})
23259          * @param {Roo.SplitBar} this
23260          * @param {Number} newSize the new width or height
23261          */
23262         "resize" : true,
23263         /**
23264          * @event moved
23265          * Fires when the splitter is moved
23266          * @param {Roo.SplitBar} this
23267          * @param {Number} newSize the new width or height
23268          */
23269         "moved" : true,
23270         /**
23271          * @event beforeresize
23272          * Fires before the splitter is dragged
23273          * @param {Roo.SplitBar} this
23274          */
23275         "beforeresize" : true,
23276
23277         "beforeapply" : true
23278     });
23279
23280     Roo.util.Observable.call(this);
23281 };
23282
23283 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23284     onStartProxyDrag : function(x, y){
23285         this.fireEvent("beforeresize", this);
23286         if(!this.overlay){
23287             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23288             o.unselectable();
23289             o.enableDisplayMode("block");
23290             // all splitbars share the same overlay
23291             Roo.SplitBar.prototype.overlay = o;
23292         }
23293         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23294         this.overlay.show();
23295         Roo.get(this.proxy).setDisplayed("block");
23296         var size = this.adapter.getElementSize(this);
23297         this.activeMinSize = this.getMinimumSize();;
23298         this.activeMaxSize = this.getMaximumSize();;
23299         var c1 = size - this.activeMinSize;
23300         var c2 = Math.max(this.activeMaxSize - size, 0);
23301         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23302             this.dd.resetConstraints();
23303             this.dd.setXConstraint(
23304                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23305                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23306             );
23307             this.dd.setYConstraint(0, 0);
23308         }else{
23309             this.dd.resetConstraints();
23310             this.dd.setXConstraint(0, 0);
23311             this.dd.setYConstraint(
23312                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23313                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23314             );
23315          }
23316         this.dragSpecs.startSize = size;
23317         this.dragSpecs.startPoint = [x, y];
23318         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23319     },
23320     
23321     /** 
23322      * @private Called after the drag operation by the DDProxy
23323      */
23324     onEndProxyDrag : function(e){
23325         Roo.get(this.proxy).setDisplayed(false);
23326         var endPoint = Roo.lib.Event.getXY(e);
23327         if(this.overlay){
23328             this.overlay.hide();
23329         }
23330         var newSize;
23331         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23332             newSize = this.dragSpecs.startSize + 
23333                 (this.placement == Roo.SplitBar.LEFT ?
23334                     endPoint[0] - this.dragSpecs.startPoint[0] :
23335                     this.dragSpecs.startPoint[0] - endPoint[0]
23336                 );
23337         }else{
23338             newSize = this.dragSpecs.startSize + 
23339                 (this.placement == Roo.SplitBar.TOP ?
23340                     endPoint[1] - this.dragSpecs.startPoint[1] :
23341                     this.dragSpecs.startPoint[1] - endPoint[1]
23342                 );
23343         }
23344         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23345         if(newSize != this.dragSpecs.startSize){
23346             if(this.fireEvent('beforeapply', this, newSize) !== false){
23347                 this.adapter.setElementSize(this, newSize);
23348                 this.fireEvent("moved", this, newSize);
23349                 this.fireEvent("resize", this, newSize);
23350             }
23351         }
23352     },
23353     
23354     /**
23355      * Get the adapter this SplitBar uses
23356      * @return The adapter object
23357      */
23358     getAdapter : function(){
23359         return this.adapter;
23360     },
23361     
23362     /**
23363      * Set the adapter this SplitBar uses
23364      * @param {Object} adapter A SplitBar adapter object
23365      */
23366     setAdapter : function(adapter){
23367         this.adapter = adapter;
23368         this.adapter.init(this);
23369     },
23370     
23371     /**
23372      * Gets the minimum size for the resizing element
23373      * @return {Number} The minimum size
23374      */
23375     getMinimumSize : function(){
23376         return this.minSize;
23377     },
23378     
23379     /**
23380      * Sets the minimum size for the resizing element
23381      * @param {Number} minSize The minimum size
23382      */
23383     setMinimumSize : function(minSize){
23384         this.minSize = minSize;
23385     },
23386     
23387     /**
23388      * Gets the maximum size for the resizing element
23389      * @return {Number} The maximum size
23390      */
23391     getMaximumSize : function(){
23392         return this.maxSize;
23393     },
23394     
23395     /**
23396      * Sets the maximum size for the resizing element
23397      * @param {Number} maxSize The maximum size
23398      */
23399     setMaximumSize : function(maxSize){
23400         this.maxSize = maxSize;
23401     },
23402     
23403     /**
23404      * Sets the initialize size for the resizing element
23405      * @param {Number} size The initial size
23406      */
23407     setCurrentSize : function(size){
23408         var oldAnimate = this.animate;
23409         this.animate = false;
23410         this.adapter.setElementSize(this, size);
23411         this.animate = oldAnimate;
23412     },
23413     
23414     /**
23415      * Destroy this splitbar. 
23416      * @param {Boolean} removeEl True to remove the element
23417      */
23418     destroy : function(removeEl){
23419         if(this.shim){
23420             this.shim.remove();
23421         }
23422         this.dd.unreg();
23423         this.proxy.parentNode.removeChild(this.proxy);
23424         if(removeEl){
23425             this.el.remove();
23426         }
23427     }
23428 });
23429
23430 /**
23431  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
23432  */
23433 Roo.SplitBar.createProxy = function(dir){
23434     var proxy = new Roo.Element(document.createElement("div"));
23435     proxy.unselectable();
23436     var cls = 'x-splitbar-proxy';
23437     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23438     document.body.appendChild(proxy.dom);
23439     return proxy.dom;
23440 };
23441
23442 /** 
23443  * @class Roo.SplitBar.BasicLayoutAdapter
23444  * Default Adapter. It assumes the splitter and resizing element are not positioned
23445  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23446  */
23447 Roo.SplitBar.BasicLayoutAdapter = function(){
23448 };
23449
23450 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23451     // do nothing for now
23452     init : function(s){
23453     
23454     },
23455     /**
23456      * Called before drag operations to get the current size of the resizing element. 
23457      * @param {Roo.SplitBar} s The SplitBar using this adapter
23458      */
23459      getElementSize : function(s){
23460         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23461             return s.resizingEl.getWidth();
23462         }else{
23463             return s.resizingEl.getHeight();
23464         }
23465     },
23466     
23467     /**
23468      * Called after drag operations to set the size of the resizing element.
23469      * @param {Roo.SplitBar} s The SplitBar using this adapter
23470      * @param {Number} newSize The new size to set
23471      * @param {Function} onComplete A function to be invoked when resizing is complete
23472      */
23473     setElementSize : function(s, newSize, onComplete){
23474         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23475             if(!s.animate){
23476                 s.resizingEl.setWidth(newSize);
23477                 if(onComplete){
23478                     onComplete(s, newSize);
23479                 }
23480             }else{
23481                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23482             }
23483         }else{
23484             
23485             if(!s.animate){
23486                 s.resizingEl.setHeight(newSize);
23487                 if(onComplete){
23488                     onComplete(s, newSize);
23489                 }
23490             }else{
23491                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23492             }
23493         }
23494     }
23495 };
23496
23497 /** 
23498  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23499  * @extends Roo.SplitBar.BasicLayoutAdapter
23500  * Adapter that  moves the splitter element to align with the resized sizing element. 
23501  * Used with an absolute positioned SplitBar.
23502  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23503  * document.body, make sure you assign an id to the body element.
23504  */
23505 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23506     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23507     this.container = Roo.get(container);
23508 };
23509
23510 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23511     init : function(s){
23512         this.basic.init(s);
23513     },
23514     
23515     getElementSize : function(s){
23516         return this.basic.getElementSize(s);
23517     },
23518     
23519     setElementSize : function(s, newSize, onComplete){
23520         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23521     },
23522     
23523     moveSplitter : function(s){
23524         var yes = Roo.SplitBar;
23525         switch(s.placement){
23526             case yes.LEFT:
23527                 s.el.setX(s.resizingEl.getRight());
23528                 break;
23529             case yes.RIGHT:
23530                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23531                 break;
23532             case yes.TOP:
23533                 s.el.setY(s.resizingEl.getBottom());
23534                 break;
23535             case yes.BOTTOM:
23536                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23537                 break;
23538         }
23539     }
23540 };
23541
23542 /**
23543  * Orientation constant - Create a vertical SplitBar
23544  * @static
23545  * @type Number
23546  */
23547 Roo.SplitBar.VERTICAL = 1;
23548
23549 /**
23550  * Orientation constant - Create a horizontal SplitBar
23551  * @static
23552  * @type Number
23553  */
23554 Roo.SplitBar.HORIZONTAL = 2;
23555
23556 /**
23557  * Placement constant - The resizing element is to the left of the splitter element
23558  * @static
23559  * @type Number
23560  */
23561 Roo.SplitBar.LEFT = 1;
23562
23563 /**
23564  * Placement constant - The resizing element is to the right of the splitter element
23565  * @static
23566  * @type Number
23567  */
23568 Roo.SplitBar.RIGHT = 2;
23569
23570 /**
23571  * Placement constant - The resizing element is positioned above the splitter element
23572  * @static
23573  * @type Number
23574  */
23575 Roo.SplitBar.TOP = 3;
23576
23577 /**
23578  * Placement constant - The resizing element is positioned under splitter element
23579  * @static
23580  * @type Number
23581  */
23582 Roo.SplitBar.BOTTOM = 4;
23583 /*
23584  * Based on:
23585  * Ext JS Library 1.1.1
23586  * Copyright(c) 2006-2007, Ext JS, LLC.
23587  *
23588  * Originally Released Under LGPL - original licence link has changed is not relivant.
23589  *
23590  * Fork - LGPL
23591  * <script type="text/javascript">
23592  */
23593
23594 /**
23595  * @class Roo.View
23596  * @extends Roo.util.Observable
23597  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23598  * This class also supports single and multi selection modes. <br>
23599  * Create a data model bound view:
23600  <pre><code>
23601  var store = new Roo.data.Store(...);
23602
23603  var view = new Roo.View({
23604     el : "my-element",
23605     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23606  
23607     singleSelect: true,
23608     selectedClass: "ydataview-selected",
23609     store: store
23610  });
23611
23612  // listen for node click?
23613  view.on("click", function(vw, index, node, e){
23614  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23615  });
23616
23617  // load XML data
23618  dataModel.load("foobar.xml");
23619  </code></pre>
23620  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23621  * <br><br>
23622  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23623  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23624  * 
23625  * Note: old style constructor is still suported (container, template, config)
23626  * 
23627  * @constructor
23628  * Create a new View
23629  * @param {Object} config The config object
23630  * 
23631  */
23632 Roo.View = function(config, depreciated_tpl, depreciated_config){
23633     
23634     if (typeof(depreciated_tpl) == 'undefined') {
23635         // new way.. - universal constructor.
23636         Roo.apply(this, config);
23637         this.el  = Roo.get(this.el);
23638     } else {
23639         // old format..
23640         this.el  = Roo.get(config);
23641         this.tpl = depreciated_tpl;
23642         Roo.apply(this, depreciated_config);
23643     }
23644      
23645     
23646     if(typeof(this.tpl) == "string"){
23647         this.tpl = new Roo.Template(this.tpl);
23648     } else {
23649         // support xtype ctors..
23650         this.tpl = new Roo.factory(this.tpl, Roo);
23651     }
23652     
23653     
23654     this.tpl.compile();
23655    
23656
23657      
23658     /** @private */
23659     this.addEvents({
23660         /**
23661          * @event beforeclick
23662          * Fires before a click is processed. Returns false to cancel the default action.
23663          * @param {Roo.View} this
23664          * @param {Number} index The index of the target node
23665          * @param {HTMLElement} node The target node
23666          * @param {Roo.EventObject} e The raw event object
23667          */
23668             "beforeclick" : true,
23669         /**
23670          * @event click
23671          * Fires when a template node is clicked.
23672          * @param {Roo.View} this
23673          * @param {Number} index The index of the target node
23674          * @param {HTMLElement} node The target node
23675          * @param {Roo.EventObject} e The raw event object
23676          */
23677             "click" : true,
23678         /**
23679          * @event dblclick
23680          * Fires when a template node is double clicked.
23681          * @param {Roo.View} this
23682          * @param {Number} index The index of the target node
23683          * @param {HTMLElement} node The target node
23684          * @param {Roo.EventObject} e The raw event object
23685          */
23686             "dblclick" : true,
23687         /**
23688          * @event contextmenu
23689          * Fires when a template node is right clicked.
23690          * @param {Roo.View} this
23691          * @param {Number} index The index of the target node
23692          * @param {HTMLElement} node The target node
23693          * @param {Roo.EventObject} e The raw event object
23694          */
23695             "contextmenu" : true,
23696         /**
23697          * @event selectionchange
23698          * Fires when the selected nodes change.
23699          * @param {Roo.View} this
23700          * @param {Array} selections Array of the selected nodes
23701          */
23702             "selectionchange" : true,
23703     
23704         /**
23705          * @event beforeselect
23706          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23707          * @param {Roo.View} this
23708          * @param {HTMLElement} node The node to be selected
23709          * @param {Array} selections Array of currently selected nodes
23710          */
23711             "beforeselect" : true,
23712         /**
23713          * @event preparedata
23714          * Fires on every row to render, to allow you to change the data.
23715          * @param {Roo.View} this
23716          * @param {Object} data to be rendered (change this)
23717          */
23718           "preparedata" : true
23719         });
23720
23721     this.el.on({
23722         "click": this.onClick,
23723         "dblclick": this.onDblClick,
23724         "contextmenu": this.onContextMenu,
23725         scope:this
23726     });
23727
23728     this.selections = [];
23729     this.nodes = [];
23730     this.cmp = new Roo.CompositeElementLite([]);
23731     if(this.store){
23732         this.store = Roo.factory(this.store, Roo.data);
23733         this.setStore(this.store, true);
23734     }
23735     Roo.View.superclass.constructor.call(this);
23736 };
23737
23738 Roo.extend(Roo.View, Roo.util.Observable, {
23739     
23740      /**
23741      * @cfg {Roo.data.Store} store Data store to load data from.
23742      */
23743     store : false,
23744     
23745     /**
23746      * @cfg {String|Roo.Element} el The container element.
23747      */
23748     el : '',
23749     
23750     /**
23751      * @cfg {String|Roo.Template} tpl The template used by this View 
23752      */
23753     tpl : false,
23754     
23755     /**
23756      * @cfg {String} selectedClass The css class to add to selected nodes
23757      */
23758     selectedClass : "x-view-selected",
23759      /**
23760      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23761      */
23762     emptyText : "",
23763     /**
23764      * @cfg {Boolean} multiSelect Allow multiple selection
23765      */
23766     multiSelect : false,
23767     /**
23768      * @cfg {Boolean} singleSelect Allow single selection
23769      */
23770     singleSelect:  false,
23771     
23772     /**
23773      * @cfg {Boolean} toggleSelect - selecting 
23774      */
23775     toggleSelect : false,
23776     
23777     /**
23778      * Returns the element this view is bound to.
23779      * @return {Roo.Element}
23780      */
23781     getEl : function(){
23782         return this.el;
23783     },
23784
23785     /**
23786      * Refreshes the view.
23787      */
23788     refresh : function(){
23789         var t = this.tpl;
23790         this.clearSelections();
23791         this.el.update("");
23792         var html = [];
23793         var records = this.store.getRange();
23794         if(records.length < 1){
23795             this.el.update(this.emptyText);
23796             return;
23797         }
23798         for(var i = 0, len = records.length; i < len; i++){
23799             var data = this.prepareData(records[i].data, i, records[i]);
23800             this.fireEvent("preparedata", this, data, i, records[i]);
23801             html[html.length] = t.apply(data);
23802         }
23803         this.el.update(html.join(""));
23804         this.nodes = this.el.dom.childNodes;
23805         this.updateIndexes(0);
23806     },
23807
23808     /**
23809      * Function to override to reformat the data that is sent to
23810      * the template for each node.
23811      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23812      * a JSON object for an UpdateManager bound view).
23813      */
23814     prepareData : function(data){
23815         return data;
23816     },
23817
23818     onUpdate : function(ds, record){
23819         this.clearSelections();
23820         var index = this.store.indexOf(record);
23821         var n = this.nodes[index];
23822         this.tpl.insertBefore(n, this.prepareData(record.data));
23823         n.parentNode.removeChild(n);
23824         this.updateIndexes(index, index);
23825     },
23826
23827     onAdd : function(ds, records, index){
23828         this.clearSelections();
23829         if(this.nodes.length == 0){
23830             this.refresh();
23831             return;
23832         }
23833         var n = this.nodes[index];
23834         for(var i = 0, len = records.length; i < len; i++){
23835             var d = this.prepareData(records[i].data);
23836             if(n){
23837                 this.tpl.insertBefore(n, d);
23838             }else{
23839                 this.tpl.append(this.el, d);
23840             }
23841         }
23842         this.updateIndexes(index);
23843     },
23844
23845     onRemove : function(ds, record, index){
23846         this.clearSelections();
23847         this.el.dom.removeChild(this.nodes[index]);
23848         this.updateIndexes(index);
23849     },
23850
23851     /**
23852      * Refresh an individual node.
23853      * @param {Number} index
23854      */
23855     refreshNode : function(index){
23856         this.onUpdate(this.store, this.store.getAt(index));
23857     },
23858
23859     updateIndexes : function(startIndex, endIndex){
23860         var ns = this.nodes;
23861         startIndex = startIndex || 0;
23862         endIndex = endIndex || ns.length - 1;
23863         for(var i = startIndex; i <= endIndex; i++){
23864             ns[i].nodeIndex = i;
23865         }
23866     },
23867
23868     /**
23869      * Changes the data store this view uses and refresh the view.
23870      * @param {Store} store
23871      */
23872     setStore : function(store, initial){
23873         if(!initial && this.store){
23874             this.store.un("datachanged", this.refresh);
23875             this.store.un("add", this.onAdd);
23876             this.store.un("remove", this.onRemove);
23877             this.store.un("update", this.onUpdate);
23878             this.store.un("clear", this.refresh);
23879         }
23880         if(store){
23881           
23882             store.on("datachanged", this.refresh, this);
23883             store.on("add", this.onAdd, this);
23884             store.on("remove", this.onRemove, this);
23885             store.on("update", this.onUpdate, this);
23886             store.on("clear", this.refresh, this);
23887         }
23888         
23889         if(store){
23890             this.refresh();
23891         }
23892     },
23893
23894     /**
23895      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23896      * @param {HTMLElement} node
23897      * @return {HTMLElement} The template node
23898      */
23899     findItemFromChild : function(node){
23900         var el = this.el.dom;
23901         if(!node || node.parentNode == el){
23902                     return node;
23903             }
23904             var p = node.parentNode;
23905             while(p && p != el){
23906             if(p.parentNode == el){
23907                 return p;
23908             }
23909             p = p.parentNode;
23910         }
23911             return null;
23912     },
23913
23914     /** @ignore */
23915     onClick : function(e){
23916         var item = this.findItemFromChild(e.getTarget());
23917         if(item){
23918             var index = this.indexOf(item);
23919             if(this.onItemClick(item, index, e) !== false){
23920                 this.fireEvent("click", this, index, item, e);
23921             }
23922         }else{
23923             this.clearSelections();
23924         }
23925     },
23926
23927     /** @ignore */
23928     onContextMenu : function(e){
23929         var item = this.findItemFromChild(e.getTarget());
23930         if(item){
23931             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23932         }
23933     },
23934
23935     /** @ignore */
23936     onDblClick : function(e){
23937         var item = this.findItemFromChild(e.getTarget());
23938         if(item){
23939             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23940         }
23941     },
23942
23943     onItemClick : function(item, index, e)
23944     {
23945         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23946             return false;
23947         }
23948         if (this.toggleSelect) {
23949             var m = this.isSelected(item) ? 'unselect' : 'select';
23950             Roo.log(m);
23951             var _t = this;
23952             _t[m](item, true, false);
23953             return true;
23954         }
23955         if(this.multiSelect || this.singleSelect){
23956             if(this.multiSelect && e.shiftKey && this.lastSelection){
23957                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23958             }else{
23959                 this.select(item, this.multiSelect && e.ctrlKey);
23960                 this.lastSelection = item;
23961             }
23962             e.preventDefault();
23963         }
23964         return true;
23965     },
23966
23967     /**
23968      * Get the number of selected nodes.
23969      * @return {Number}
23970      */
23971     getSelectionCount : function(){
23972         return this.selections.length;
23973     },
23974
23975     /**
23976      * Get the currently selected nodes.
23977      * @return {Array} An array of HTMLElements
23978      */
23979     getSelectedNodes : function(){
23980         return this.selections;
23981     },
23982
23983     /**
23984      * Get the indexes of the selected nodes.
23985      * @return {Array}
23986      */
23987     getSelectedIndexes : function(){
23988         var indexes = [], s = this.selections;
23989         for(var i = 0, len = s.length; i < len; i++){
23990             indexes.push(s[i].nodeIndex);
23991         }
23992         return indexes;
23993     },
23994
23995     /**
23996      * Clear all selections
23997      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23998      */
23999     clearSelections : function(suppressEvent){
24000         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
24001             this.cmp.elements = this.selections;
24002             this.cmp.removeClass(this.selectedClass);
24003             this.selections = [];
24004             if(!suppressEvent){
24005                 this.fireEvent("selectionchange", this, this.selections);
24006             }
24007         }
24008     },
24009
24010     /**
24011      * Returns true if the passed node is selected
24012      * @param {HTMLElement/Number} node The node or node index
24013      * @return {Boolean}
24014      */
24015     isSelected : function(node){
24016         var s = this.selections;
24017         if(s.length < 1){
24018             return false;
24019         }
24020         node = this.getNode(node);
24021         return s.indexOf(node) !== -1;
24022     },
24023
24024     /**
24025      * Selects nodes.
24026      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
24027      * @param {Boolean} keepExisting (optional) true to keep existing selections
24028      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24029      */
24030     select : function(nodeInfo, keepExisting, suppressEvent){
24031         if(nodeInfo instanceof Array){
24032             if(!keepExisting){
24033                 this.clearSelections(true);
24034             }
24035             for(var i = 0, len = nodeInfo.length; i < len; i++){
24036                 this.select(nodeInfo[i], true, true);
24037             }
24038             return;
24039         } 
24040         var node = this.getNode(nodeInfo);
24041         if(!node || this.isSelected(node)){
24042             return; // already selected.
24043         }
24044         if(!keepExisting){
24045             this.clearSelections(true);
24046         }
24047         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24048             Roo.fly(node).addClass(this.selectedClass);
24049             this.selections.push(node);
24050             if(!suppressEvent){
24051                 this.fireEvent("selectionchange", this, this.selections);
24052             }
24053         }
24054         
24055         
24056     },
24057       /**
24058      * Unselects nodes.
24059      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
24060      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24061      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24062      */
24063     unselect : function(nodeInfo, keepExisting, suppressEvent)
24064     {
24065         if(nodeInfo instanceof Array){
24066             Roo.each(this.selections, function(s) {
24067                 this.unselect(s, nodeInfo);
24068             }, this);
24069             return;
24070         }
24071         var node = this.getNode(nodeInfo);
24072         if(!node || !this.isSelected(node)){
24073             Roo.log("not selected");
24074             return; // not selected.
24075         }
24076         // fireevent???
24077         var ns = [];
24078         Roo.each(this.selections, function(s) {
24079             if (s == node ) {
24080                 Roo.fly(node).removeClass(this.selectedClass);
24081
24082                 return;
24083             }
24084             ns.push(s);
24085         },this);
24086         
24087         this.selections= ns;
24088         this.fireEvent("selectionchange", this, this.selections);
24089     },
24090
24091     /**
24092      * Gets a template node.
24093      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24094      * @return {HTMLElement} The node or null if it wasn't found
24095      */
24096     getNode : function(nodeInfo){
24097         if(typeof nodeInfo == "string"){
24098             return document.getElementById(nodeInfo);
24099         }else if(typeof nodeInfo == "number"){
24100             return this.nodes[nodeInfo];
24101         }
24102         return nodeInfo;
24103     },
24104
24105     /**
24106      * Gets a range template nodes.
24107      * @param {Number} startIndex
24108      * @param {Number} endIndex
24109      * @return {Array} An array of nodes
24110      */
24111     getNodes : function(start, end){
24112         var ns = this.nodes;
24113         start = start || 0;
24114         end = typeof end == "undefined" ? ns.length - 1 : end;
24115         var nodes = [];
24116         if(start <= end){
24117             for(var i = start; i <= end; i++){
24118                 nodes.push(ns[i]);
24119             }
24120         } else{
24121             for(var i = start; i >= end; i--){
24122                 nodes.push(ns[i]);
24123             }
24124         }
24125         return nodes;
24126     },
24127
24128     /**
24129      * Finds the index of the passed node
24130      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24131      * @return {Number} The index of the node or -1
24132      */
24133     indexOf : function(node){
24134         node = this.getNode(node);
24135         if(typeof node.nodeIndex == "number"){
24136             return node.nodeIndex;
24137         }
24138         var ns = this.nodes;
24139         for(var i = 0, len = ns.length; i < len; i++){
24140             if(ns[i] == node){
24141                 return i;
24142             }
24143         }
24144         return -1;
24145     }
24146 });
24147 /*
24148  * Based on:
24149  * Ext JS Library 1.1.1
24150  * Copyright(c) 2006-2007, Ext JS, LLC.
24151  *
24152  * Originally Released Under LGPL - original licence link has changed is not relivant.
24153  *
24154  * Fork - LGPL
24155  * <script type="text/javascript">
24156  */
24157
24158 /**
24159  * @class Roo.JsonView
24160  * @extends Roo.View
24161  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24162 <pre><code>
24163 var view = new Roo.JsonView({
24164     container: "my-element",
24165     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24166     multiSelect: true, 
24167     jsonRoot: "data" 
24168 });
24169
24170 // listen for node click?
24171 view.on("click", function(vw, index, node, e){
24172     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24173 });
24174
24175 // direct load of JSON data
24176 view.load("foobar.php");
24177
24178 // Example from my blog list
24179 var tpl = new Roo.Template(
24180     '&lt;div class="entry"&gt;' +
24181     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24182     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24183     "&lt;/div&gt;&lt;hr /&gt;"
24184 );
24185
24186 var moreView = new Roo.JsonView({
24187     container :  "entry-list", 
24188     template : tpl,
24189     jsonRoot: "posts"
24190 });
24191 moreView.on("beforerender", this.sortEntries, this);
24192 moreView.load({
24193     url: "/blog/get-posts.php",
24194     params: "allposts=true",
24195     text: "Loading Blog Entries..."
24196 });
24197 </code></pre>
24198
24199 * Note: old code is supported with arguments : (container, template, config)
24200
24201
24202  * @constructor
24203  * Create a new JsonView
24204  * 
24205  * @param {Object} config The config object
24206  * 
24207  */
24208 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24209     
24210     
24211     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24212
24213     var um = this.el.getUpdateManager();
24214     um.setRenderer(this);
24215     um.on("update", this.onLoad, this);
24216     um.on("failure", this.onLoadException, this);
24217
24218     /**
24219      * @event beforerender
24220      * Fires before rendering of the downloaded JSON data.
24221      * @param {Roo.JsonView} this
24222      * @param {Object} data The JSON data loaded
24223      */
24224     /**
24225      * @event load
24226      * Fires when data is loaded.
24227      * @param {Roo.JsonView} this
24228      * @param {Object} data The JSON data loaded
24229      * @param {Object} response The raw Connect response object
24230      */
24231     /**
24232      * @event loadexception
24233      * Fires when loading fails.
24234      * @param {Roo.JsonView} this
24235      * @param {Object} response The raw Connect response object
24236      */
24237     this.addEvents({
24238         'beforerender' : true,
24239         'load' : true,
24240         'loadexception' : true
24241     });
24242 };
24243 Roo.extend(Roo.JsonView, Roo.View, {
24244     /**
24245      * @type {String} The root property in the loaded JSON object that contains the data
24246      */
24247     jsonRoot : "",
24248
24249     /**
24250      * Refreshes the view.
24251      */
24252     refresh : function(){
24253         this.clearSelections();
24254         this.el.update("");
24255         var html = [];
24256         var o = this.jsonData;
24257         if(o && o.length > 0){
24258             for(var i = 0, len = o.length; i < len; i++){
24259                 var data = this.prepareData(o[i], i, o);
24260                 html[html.length] = this.tpl.apply(data);
24261             }
24262         }else{
24263             html.push(this.emptyText);
24264         }
24265         this.el.update(html.join(""));
24266         this.nodes = this.el.dom.childNodes;
24267         this.updateIndexes(0);
24268     },
24269
24270     /**
24271      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
24272      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
24273      <pre><code>
24274      view.load({
24275          url: "your-url.php",
24276          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24277          callback: yourFunction,
24278          scope: yourObject, //(optional scope)
24279          discardUrl: false,
24280          nocache: false,
24281          text: "Loading...",
24282          timeout: 30,
24283          scripts: false
24284      });
24285      </code></pre>
24286      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24287      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
24288      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
24289      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24290      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
24291      */
24292     load : function(){
24293         var um = this.el.getUpdateManager();
24294         um.update.apply(um, arguments);
24295     },
24296
24297     render : function(el, response){
24298         this.clearSelections();
24299         this.el.update("");
24300         var o;
24301         try{
24302             o = Roo.util.JSON.decode(response.responseText);
24303             if(this.jsonRoot){
24304                 
24305                 o = o[this.jsonRoot];
24306             }
24307         } catch(e){
24308         }
24309         /**
24310          * The current JSON data or null
24311          */
24312         this.jsonData = o;
24313         this.beforeRender();
24314         this.refresh();
24315     },
24316
24317 /**
24318  * Get the number of records in the current JSON dataset
24319  * @return {Number}
24320  */
24321     getCount : function(){
24322         return this.jsonData ? this.jsonData.length : 0;
24323     },
24324
24325 /**
24326  * Returns the JSON object for the specified node(s)
24327  * @param {HTMLElement/Array} node The node or an array of nodes
24328  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24329  * you get the JSON object for the node
24330  */
24331     getNodeData : function(node){
24332         if(node instanceof Array){
24333             var data = [];
24334             for(var i = 0, len = node.length; i < len; i++){
24335                 data.push(this.getNodeData(node[i]));
24336             }
24337             return data;
24338         }
24339         return this.jsonData[this.indexOf(node)] || null;
24340     },
24341
24342     beforeRender : function(){
24343         this.snapshot = this.jsonData;
24344         if(this.sortInfo){
24345             this.sort.apply(this, this.sortInfo);
24346         }
24347         this.fireEvent("beforerender", this, this.jsonData);
24348     },
24349
24350     onLoad : function(el, o){
24351         this.fireEvent("load", this, this.jsonData, o);
24352     },
24353
24354     onLoadException : function(el, o){
24355         this.fireEvent("loadexception", this, o);
24356     },
24357
24358 /**
24359  * Filter the data by a specific property.
24360  * @param {String} property A property on your JSON objects
24361  * @param {String/RegExp} value Either string that the property values
24362  * should start with, or a RegExp to test against the property
24363  */
24364     filter : function(property, value){
24365         if(this.jsonData){
24366             var data = [];
24367             var ss = this.snapshot;
24368             if(typeof value == "string"){
24369                 var vlen = value.length;
24370                 if(vlen == 0){
24371                     this.clearFilter();
24372                     return;
24373                 }
24374                 value = value.toLowerCase();
24375                 for(var i = 0, len = ss.length; i < len; i++){
24376                     var o = ss[i];
24377                     if(o[property].substr(0, vlen).toLowerCase() == value){
24378                         data.push(o);
24379                     }
24380                 }
24381             } else if(value.exec){ // regex?
24382                 for(var i = 0, len = ss.length; i < len; i++){
24383                     var o = ss[i];
24384                     if(value.test(o[property])){
24385                         data.push(o);
24386                     }
24387                 }
24388             } else{
24389                 return;
24390             }
24391             this.jsonData = data;
24392             this.refresh();
24393         }
24394     },
24395
24396 /**
24397  * Filter by a function. The passed function will be called with each
24398  * object in the current dataset. If the function returns true the value is kept,
24399  * otherwise it is filtered.
24400  * @param {Function} fn
24401  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24402  */
24403     filterBy : function(fn, scope){
24404         if(this.jsonData){
24405             var data = [];
24406             var ss = this.snapshot;
24407             for(var i = 0, len = ss.length; i < len; i++){
24408                 var o = ss[i];
24409                 if(fn.call(scope || this, o)){
24410                     data.push(o);
24411                 }
24412             }
24413             this.jsonData = data;
24414             this.refresh();
24415         }
24416     },
24417
24418 /**
24419  * Clears the current filter.
24420  */
24421     clearFilter : function(){
24422         if(this.snapshot && this.jsonData != this.snapshot){
24423             this.jsonData = this.snapshot;
24424             this.refresh();
24425         }
24426     },
24427
24428
24429 /**
24430  * Sorts the data for this view and refreshes it.
24431  * @param {String} property A property on your JSON objects to sort on
24432  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24433  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24434  */
24435     sort : function(property, dir, sortType){
24436         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24437         if(this.jsonData){
24438             var p = property;
24439             var dsc = dir && dir.toLowerCase() == "desc";
24440             var f = function(o1, o2){
24441                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24442                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24443                 ;
24444                 if(v1 < v2){
24445                     return dsc ? +1 : -1;
24446                 } else if(v1 > v2){
24447                     return dsc ? -1 : +1;
24448                 } else{
24449                     return 0;
24450                 }
24451             };
24452             this.jsonData.sort(f);
24453             this.refresh();
24454             if(this.jsonData != this.snapshot){
24455                 this.snapshot.sort(f);
24456             }
24457         }
24458     }
24459 });/*
24460  * Based on:
24461  * Ext JS Library 1.1.1
24462  * Copyright(c) 2006-2007, Ext JS, LLC.
24463  *
24464  * Originally Released Under LGPL - original licence link has changed is not relivant.
24465  *
24466  * Fork - LGPL
24467  * <script type="text/javascript">
24468  */
24469  
24470
24471 /**
24472  * @class Roo.ColorPalette
24473  * @extends Roo.Component
24474  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24475  * Here's an example of typical usage:
24476  * <pre><code>
24477 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24478 cp.render('my-div');
24479
24480 cp.on('select', function(palette, selColor){
24481     // do something with selColor
24482 });
24483 </code></pre>
24484  * @constructor
24485  * Create a new ColorPalette
24486  * @param {Object} config The config object
24487  */
24488 Roo.ColorPalette = function(config){
24489     Roo.ColorPalette.superclass.constructor.call(this, config);
24490     this.addEvents({
24491         /**
24492              * @event select
24493              * Fires when a color is selected
24494              * @param {ColorPalette} this
24495              * @param {String} color The 6-digit color hex code (without the # symbol)
24496              */
24497         select: true
24498     });
24499
24500     if(this.handler){
24501         this.on("select", this.handler, this.scope, true);
24502     }
24503 };
24504 Roo.extend(Roo.ColorPalette, Roo.Component, {
24505     /**
24506      * @cfg {String} itemCls
24507      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24508      */
24509     itemCls : "x-color-palette",
24510     /**
24511      * @cfg {String} value
24512      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24513      * the hex codes are case-sensitive.
24514      */
24515     value : null,
24516     clickEvent:'click',
24517     // private
24518     ctype: "Roo.ColorPalette",
24519
24520     /**
24521      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24522      */
24523     allowReselect : false,
24524
24525     /**
24526      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24527      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24528      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24529      * of colors with the width setting until the box is symmetrical.</p>
24530      * <p>You can override individual colors if needed:</p>
24531      * <pre><code>
24532 var cp = new Roo.ColorPalette();
24533 cp.colors[0] = "FF0000";  // change the first box to red
24534 </code></pre>
24535
24536 Or you can provide a custom array of your own for complete control:
24537 <pre><code>
24538 var cp = new Roo.ColorPalette();
24539 cp.colors = ["000000", "993300", "333300"];
24540 </code></pre>
24541      * @type Array
24542      */
24543     colors : [
24544         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24545         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24546         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24547         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24548         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24549     ],
24550
24551     // private
24552     onRender : function(container, position){
24553         var t = new Roo.MasterTemplate(
24554             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24555         );
24556         var c = this.colors;
24557         for(var i = 0, len = c.length; i < len; i++){
24558             t.add([c[i]]);
24559         }
24560         var el = document.createElement("div");
24561         el.className = this.itemCls;
24562         t.overwrite(el);
24563         container.dom.insertBefore(el, position);
24564         this.el = Roo.get(el);
24565         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24566         if(this.clickEvent != 'click'){
24567             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24568         }
24569     },
24570
24571     // private
24572     afterRender : function(){
24573         Roo.ColorPalette.superclass.afterRender.call(this);
24574         if(this.value){
24575             var s = this.value;
24576             this.value = null;
24577             this.select(s);
24578         }
24579     },
24580
24581     // private
24582     handleClick : function(e, t){
24583         e.preventDefault();
24584         if(!this.disabled){
24585             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24586             this.select(c.toUpperCase());
24587         }
24588     },
24589
24590     /**
24591      * Selects the specified color in the palette (fires the select event)
24592      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24593      */
24594     select : function(color){
24595         color = color.replace("#", "");
24596         if(color != this.value || this.allowReselect){
24597             var el = this.el;
24598             if(this.value){
24599                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24600             }
24601             el.child("a.color-"+color).addClass("x-color-palette-sel");
24602             this.value = color;
24603             this.fireEvent("select", this, color);
24604         }
24605     }
24606 });/*
24607  * Based on:
24608  * Ext JS Library 1.1.1
24609  * Copyright(c) 2006-2007, Ext JS, LLC.
24610  *
24611  * Originally Released Under LGPL - original licence link has changed is not relivant.
24612  *
24613  * Fork - LGPL
24614  * <script type="text/javascript">
24615  */
24616  
24617 /**
24618  * @class Roo.DatePicker
24619  * @extends Roo.Component
24620  * Simple date picker class.
24621  * @constructor
24622  * Create a new DatePicker
24623  * @param {Object} config The config object
24624  */
24625 Roo.DatePicker = function(config){
24626     Roo.DatePicker.superclass.constructor.call(this, config);
24627
24628     this.value = config && config.value ?
24629                  config.value.clearTime() : new Date().clearTime();
24630
24631     this.addEvents({
24632         /**
24633              * @event select
24634              * Fires when a date is selected
24635              * @param {DatePicker} this
24636              * @param {Date} date The selected date
24637              */
24638         'select': true,
24639         /**
24640              * @event monthchange
24641              * Fires when the displayed month changes 
24642              * @param {DatePicker} this
24643              * @param {Date} date The selected month
24644              */
24645         'monthchange': true
24646     });
24647
24648     if(this.handler){
24649         this.on("select", this.handler,  this.scope || this);
24650     }
24651     // build the disabledDatesRE
24652     if(!this.disabledDatesRE && this.disabledDates){
24653         var dd = this.disabledDates;
24654         var re = "(?:";
24655         for(var i = 0; i < dd.length; i++){
24656             re += dd[i];
24657             if(i != dd.length-1) re += "|";
24658         }
24659         this.disabledDatesRE = new RegExp(re + ")");
24660     }
24661 };
24662
24663 Roo.extend(Roo.DatePicker, Roo.Component, {
24664     /**
24665      * @cfg {String} todayText
24666      * The text to display on the button that selects the current date (defaults to "Today")
24667      */
24668     todayText : "Today",
24669     /**
24670      * @cfg {String} okText
24671      * The text to display on the ok button
24672      */
24673     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24674     /**
24675      * @cfg {String} cancelText
24676      * The text to display on the cancel button
24677      */
24678     cancelText : "Cancel",
24679     /**
24680      * @cfg {String} todayTip
24681      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24682      */
24683     todayTip : "{0} (Spacebar)",
24684     /**
24685      * @cfg {Date} minDate
24686      * Minimum allowable date (JavaScript date object, defaults to null)
24687      */
24688     minDate : null,
24689     /**
24690      * @cfg {Date} maxDate
24691      * Maximum allowable date (JavaScript date object, defaults to null)
24692      */
24693     maxDate : null,
24694     /**
24695      * @cfg {String} minText
24696      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24697      */
24698     minText : "This date is before the minimum date",
24699     /**
24700      * @cfg {String} maxText
24701      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24702      */
24703     maxText : "This date is after the maximum date",
24704     /**
24705      * @cfg {String} format
24706      * The default date format string which can be overriden for localization support.  The format must be
24707      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24708      */
24709     format : "m/d/y",
24710     /**
24711      * @cfg {Array} disabledDays
24712      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24713      */
24714     disabledDays : null,
24715     /**
24716      * @cfg {String} disabledDaysText
24717      * The tooltip to display when the date falls on a disabled day (defaults to "")
24718      */
24719     disabledDaysText : "",
24720     /**
24721      * @cfg {RegExp} disabledDatesRE
24722      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24723      */
24724     disabledDatesRE : null,
24725     /**
24726      * @cfg {String} disabledDatesText
24727      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24728      */
24729     disabledDatesText : "",
24730     /**
24731      * @cfg {Boolean} constrainToViewport
24732      * True to constrain the date picker to the viewport (defaults to true)
24733      */
24734     constrainToViewport : true,
24735     /**
24736      * @cfg {Array} monthNames
24737      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24738      */
24739     monthNames : Date.monthNames,
24740     /**
24741      * @cfg {Array} dayNames
24742      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24743      */
24744     dayNames : Date.dayNames,
24745     /**
24746      * @cfg {String} nextText
24747      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24748      */
24749     nextText: 'Next Month (Control+Right)',
24750     /**
24751      * @cfg {String} prevText
24752      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24753      */
24754     prevText: 'Previous Month (Control+Left)',
24755     /**
24756      * @cfg {String} monthYearText
24757      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24758      */
24759     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24760     /**
24761      * @cfg {Number} startDay
24762      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24763      */
24764     startDay : 0,
24765     /**
24766      * @cfg {Bool} showClear
24767      * Show a clear button (usefull for date form elements that can be blank.)
24768      */
24769     
24770     showClear: false,
24771     
24772     /**
24773      * Sets the value of the date field
24774      * @param {Date} value The date to set
24775      */
24776     setValue : function(value){
24777         var old = this.value;
24778         this.value = value.clearTime(true);
24779         if(this.el){
24780             this.update(this.value);
24781         }
24782     },
24783
24784     /**
24785      * Gets the current selected value of the date field
24786      * @return {Date} The selected date
24787      */
24788     getValue : function(){
24789         return this.value;
24790     },
24791
24792     // private
24793     focus : function(){
24794         if(this.el){
24795             this.update(this.activeDate);
24796         }
24797     },
24798
24799     // private
24800     onRender : function(container, position){
24801         var m = [
24802              '<table cellspacing="0">',
24803                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
24804                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24805         var dn = this.dayNames;
24806         for(var i = 0; i < 7; i++){
24807             var d = this.startDay+i;
24808             if(d > 6){
24809                 d = d-7;
24810             }
24811             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24812         }
24813         m[m.length] = "</tr></thead><tbody><tr>";
24814         for(var i = 0; i < 42; i++) {
24815             if(i % 7 == 0 && i != 0){
24816                 m[m.length] = "</tr><tr>";
24817             }
24818             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24819         }
24820         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24821             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24822
24823         var el = document.createElement("div");
24824         el.className = "x-date-picker";
24825         el.innerHTML = m.join("");
24826
24827         container.dom.insertBefore(el, position);
24828
24829         this.el = Roo.get(el);
24830         this.eventEl = Roo.get(el.firstChild);
24831
24832         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24833             handler: this.showPrevMonth,
24834             scope: this,
24835             preventDefault:true,
24836             stopDefault:true
24837         });
24838
24839         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24840             handler: this.showNextMonth,
24841             scope: this,
24842             preventDefault:true,
24843             stopDefault:true
24844         });
24845
24846         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24847
24848         this.monthPicker = this.el.down('div.x-date-mp');
24849         this.monthPicker.enableDisplayMode('block');
24850         
24851         var kn = new Roo.KeyNav(this.eventEl, {
24852             "left" : function(e){
24853                 e.ctrlKey ?
24854                     this.showPrevMonth() :
24855                     this.update(this.activeDate.add("d", -1));
24856             },
24857
24858             "right" : function(e){
24859                 e.ctrlKey ?
24860                     this.showNextMonth() :
24861                     this.update(this.activeDate.add("d", 1));
24862             },
24863
24864             "up" : function(e){
24865                 e.ctrlKey ?
24866                     this.showNextYear() :
24867                     this.update(this.activeDate.add("d", -7));
24868             },
24869
24870             "down" : function(e){
24871                 e.ctrlKey ?
24872                     this.showPrevYear() :
24873                     this.update(this.activeDate.add("d", 7));
24874             },
24875
24876             "pageUp" : function(e){
24877                 this.showNextMonth();
24878             },
24879
24880             "pageDown" : function(e){
24881                 this.showPrevMonth();
24882             },
24883
24884             "enter" : function(e){
24885                 e.stopPropagation();
24886                 return true;
24887             },
24888
24889             scope : this
24890         });
24891
24892         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24893
24894         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24895
24896         this.el.unselectable();
24897         
24898         this.cells = this.el.select("table.x-date-inner tbody td");
24899         this.textNodes = this.el.query("table.x-date-inner tbody span");
24900
24901         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24902             text: "&#160;",
24903             tooltip: this.monthYearText
24904         });
24905
24906         this.mbtn.on('click', this.showMonthPicker, this);
24907         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24908
24909
24910         var today = (new Date()).dateFormat(this.format);
24911         
24912         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24913         if (this.showClear) {
24914             baseTb.add( new Roo.Toolbar.Fill());
24915         }
24916         baseTb.add({
24917             text: String.format(this.todayText, today),
24918             tooltip: String.format(this.todayTip, today),
24919             handler: this.selectToday,
24920             scope: this
24921         });
24922         
24923         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24924             
24925         //});
24926         if (this.showClear) {
24927             
24928             baseTb.add( new Roo.Toolbar.Fill());
24929             baseTb.add({
24930                 text: '&#160;',
24931                 cls: 'x-btn-icon x-btn-clear',
24932                 handler: function() {
24933                     //this.value = '';
24934                     this.fireEvent("select", this, '');
24935                 },
24936                 scope: this
24937             });
24938         }
24939         
24940         
24941         if(Roo.isIE){
24942             this.el.repaint();
24943         }
24944         this.update(this.value);
24945     },
24946
24947     createMonthPicker : function(){
24948         if(!this.monthPicker.dom.firstChild){
24949             var buf = ['<table border="0" cellspacing="0">'];
24950             for(var i = 0; i < 6; i++){
24951                 buf.push(
24952                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24953                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24954                     i == 0 ?
24955                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
24956                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24957                 );
24958             }
24959             buf.push(
24960                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24961                     this.okText,
24962                     '</button><button type="button" class="x-date-mp-cancel">',
24963                     this.cancelText,
24964                     '</button></td></tr>',
24965                 '</table>'
24966             );
24967             this.monthPicker.update(buf.join(''));
24968             this.monthPicker.on('click', this.onMonthClick, this);
24969             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24970
24971             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24972             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24973
24974             this.mpMonths.each(function(m, a, i){
24975                 i += 1;
24976                 if((i%2) == 0){
24977                     m.dom.xmonth = 5 + Math.round(i * .5);
24978                 }else{
24979                     m.dom.xmonth = Math.round((i-1) * .5);
24980                 }
24981             });
24982         }
24983     },
24984
24985     showMonthPicker : function(){
24986         this.createMonthPicker();
24987         var size = this.el.getSize();
24988         this.monthPicker.setSize(size);
24989         this.monthPicker.child('table').setSize(size);
24990
24991         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24992         this.updateMPMonth(this.mpSelMonth);
24993         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24994         this.updateMPYear(this.mpSelYear);
24995
24996         this.monthPicker.slideIn('t', {duration:.2});
24997     },
24998
24999     updateMPYear : function(y){
25000         this.mpyear = y;
25001         var ys = this.mpYears.elements;
25002         for(var i = 1; i <= 10; i++){
25003             var td = ys[i-1], y2;
25004             if((i%2) == 0){
25005                 y2 = y + Math.round(i * .5);
25006                 td.firstChild.innerHTML = y2;
25007                 td.xyear = y2;
25008             }else{
25009                 y2 = y - (5-Math.round(i * .5));
25010                 td.firstChild.innerHTML = y2;
25011                 td.xyear = y2;
25012             }
25013             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25014         }
25015     },
25016
25017     updateMPMonth : function(sm){
25018         this.mpMonths.each(function(m, a, i){
25019             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25020         });
25021     },
25022
25023     selectMPMonth: function(m){
25024         
25025     },
25026
25027     onMonthClick : function(e, t){
25028         e.stopEvent();
25029         var el = new Roo.Element(t), pn;
25030         if(el.is('button.x-date-mp-cancel')){
25031             this.hideMonthPicker();
25032         }
25033         else if(el.is('button.x-date-mp-ok')){
25034             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25035             this.hideMonthPicker();
25036         }
25037         else if(pn = el.up('td.x-date-mp-month', 2)){
25038             this.mpMonths.removeClass('x-date-mp-sel');
25039             pn.addClass('x-date-mp-sel');
25040             this.mpSelMonth = pn.dom.xmonth;
25041         }
25042         else if(pn = el.up('td.x-date-mp-year', 2)){
25043             this.mpYears.removeClass('x-date-mp-sel');
25044             pn.addClass('x-date-mp-sel');
25045             this.mpSelYear = pn.dom.xyear;
25046         }
25047         else if(el.is('a.x-date-mp-prev')){
25048             this.updateMPYear(this.mpyear-10);
25049         }
25050         else if(el.is('a.x-date-mp-next')){
25051             this.updateMPYear(this.mpyear+10);
25052         }
25053     },
25054
25055     onMonthDblClick : function(e, t){
25056         e.stopEvent();
25057         var el = new Roo.Element(t), pn;
25058         if(pn = el.up('td.x-date-mp-month', 2)){
25059             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25060             this.hideMonthPicker();
25061         }
25062         else if(pn = el.up('td.x-date-mp-year', 2)){
25063             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25064             this.hideMonthPicker();
25065         }
25066     },
25067
25068     hideMonthPicker : function(disableAnim){
25069         if(this.monthPicker){
25070             if(disableAnim === true){
25071                 this.monthPicker.hide();
25072             }else{
25073                 this.monthPicker.slideOut('t', {duration:.2});
25074             }
25075         }
25076     },
25077
25078     // private
25079     showPrevMonth : function(e){
25080         this.update(this.activeDate.add("mo", -1));
25081     },
25082
25083     // private
25084     showNextMonth : function(e){
25085         this.update(this.activeDate.add("mo", 1));
25086     },
25087
25088     // private
25089     showPrevYear : function(){
25090         this.update(this.activeDate.add("y", -1));
25091     },
25092
25093     // private
25094     showNextYear : function(){
25095         this.update(this.activeDate.add("y", 1));
25096     },
25097
25098     // private
25099     handleMouseWheel : function(e){
25100         var delta = e.getWheelDelta();
25101         if(delta > 0){
25102             this.showPrevMonth();
25103             e.stopEvent();
25104         } else if(delta < 0){
25105             this.showNextMonth();
25106             e.stopEvent();
25107         }
25108     },
25109
25110     // private
25111     handleDateClick : function(e, t){
25112         e.stopEvent();
25113         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25114             this.setValue(new Date(t.dateValue));
25115             this.fireEvent("select", this, this.value);
25116         }
25117     },
25118
25119     // private
25120     selectToday : function(){
25121         this.setValue(new Date().clearTime());
25122         this.fireEvent("select", this, this.value);
25123     },
25124
25125     // private
25126     update : function(date)
25127     {
25128         var vd = this.activeDate;
25129         this.activeDate = date;
25130         if(vd && this.el){
25131             var t = date.getTime();
25132             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25133                 this.cells.removeClass("x-date-selected");
25134                 this.cells.each(function(c){
25135                    if(c.dom.firstChild.dateValue == t){
25136                        c.addClass("x-date-selected");
25137                        setTimeout(function(){
25138                             try{c.dom.firstChild.focus();}catch(e){}
25139                        }, 50);
25140                        return false;
25141                    }
25142                 });
25143                 return;
25144             }
25145         }
25146         
25147         var days = date.getDaysInMonth();
25148         var firstOfMonth = date.getFirstDateOfMonth();
25149         var startingPos = firstOfMonth.getDay()-this.startDay;
25150
25151         if(startingPos <= this.startDay){
25152             startingPos += 7;
25153         }
25154
25155         var pm = date.add("mo", -1);
25156         var prevStart = pm.getDaysInMonth()-startingPos;
25157
25158         var cells = this.cells.elements;
25159         var textEls = this.textNodes;
25160         days += startingPos;
25161
25162         // convert everything to numbers so it's fast
25163         var day = 86400000;
25164         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25165         var today = new Date().clearTime().getTime();
25166         var sel = date.clearTime().getTime();
25167         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25168         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25169         var ddMatch = this.disabledDatesRE;
25170         var ddText = this.disabledDatesText;
25171         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25172         var ddaysText = this.disabledDaysText;
25173         var format = this.format;
25174
25175         var setCellClass = function(cal, cell){
25176             cell.title = "";
25177             var t = d.getTime();
25178             cell.firstChild.dateValue = t;
25179             if(t == today){
25180                 cell.className += " x-date-today";
25181                 cell.title = cal.todayText;
25182             }
25183             if(t == sel){
25184                 cell.className += " x-date-selected";
25185                 setTimeout(function(){
25186                     try{cell.firstChild.focus();}catch(e){}
25187                 }, 50);
25188             }
25189             // disabling
25190             if(t < min) {
25191                 cell.className = " x-date-disabled";
25192                 cell.title = cal.minText;
25193                 return;
25194             }
25195             if(t > max) {
25196                 cell.className = " x-date-disabled";
25197                 cell.title = cal.maxText;
25198                 return;
25199             }
25200             if(ddays){
25201                 if(ddays.indexOf(d.getDay()) != -1){
25202                     cell.title = ddaysText;
25203                     cell.className = " x-date-disabled";
25204                 }
25205             }
25206             if(ddMatch && format){
25207                 var fvalue = d.dateFormat(format);
25208                 if(ddMatch.test(fvalue)){
25209                     cell.title = ddText.replace("%0", fvalue);
25210                     cell.className = " x-date-disabled";
25211                 }
25212             }
25213         };
25214
25215         var i = 0;
25216         for(; i < startingPos; i++) {
25217             textEls[i].innerHTML = (++prevStart);
25218             d.setDate(d.getDate()+1);
25219             cells[i].className = "x-date-prevday";
25220             setCellClass(this, cells[i]);
25221         }
25222         for(; i < days; i++){
25223             intDay = i - startingPos + 1;
25224             textEls[i].innerHTML = (intDay);
25225             d.setDate(d.getDate()+1);
25226             cells[i].className = "x-date-active";
25227             setCellClass(this, cells[i]);
25228         }
25229         var extraDays = 0;
25230         for(; i < 42; i++) {
25231              textEls[i].innerHTML = (++extraDays);
25232              d.setDate(d.getDate()+1);
25233              cells[i].className = "x-date-nextday";
25234              setCellClass(this, cells[i]);
25235         }
25236
25237         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25238         this.fireEvent('monthchange', this, date);
25239         
25240         if(!this.internalRender){
25241             var main = this.el.dom.firstChild;
25242             var w = main.offsetWidth;
25243             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25244             Roo.fly(main).setWidth(w);
25245             this.internalRender = true;
25246             // opera does not respect the auto grow header center column
25247             // then, after it gets a width opera refuses to recalculate
25248             // without a second pass
25249             if(Roo.isOpera && !this.secondPass){
25250                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25251                 this.secondPass = true;
25252                 this.update.defer(10, this, [date]);
25253             }
25254         }
25255         
25256         
25257     }
25258 });        /*
25259  * Based on:
25260  * Ext JS Library 1.1.1
25261  * Copyright(c) 2006-2007, Ext JS, LLC.
25262  *
25263  * Originally Released Under LGPL - original licence link has changed is not relivant.
25264  *
25265  * Fork - LGPL
25266  * <script type="text/javascript">
25267  */
25268 /**
25269  * @class Roo.TabPanel
25270  * @extends Roo.util.Observable
25271  * A lightweight tab container.
25272  * <br><br>
25273  * Usage:
25274  * <pre><code>
25275 // basic tabs 1, built from existing content
25276 var tabs = new Roo.TabPanel("tabs1");
25277 tabs.addTab("script", "View Script");
25278 tabs.addTab("markup", "View Markup");
25279 tabs.activate("script");
25280
25281 // more advanced tabs, built from javascript
25282 var jtabs = new Roo.TabPanel("jtabs");
25283 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25284
25285 // set up the UpdateManager
25286 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25287 var updater = tab2.getUpdateManager();
25288 updater.setDefaultUrl("ajax1.htm");
25289 tab2.on('activate', updater.refresh, updater, true);
25290
25291 // Use setUrl for Ajax loading
25292 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25293 tab3.setUrl("ajax2.htm", null, true);
25294
25295 // Disabled tab
25296 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25297 tab4.disable();
25298
25299 jtabs.activate("jtabs-1");
25300  * </code></pre>
25301  * @constructor
25302  * Create a new TabPanel.
25303  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25304  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25305  */
25306 Roo.TabPanel = function(container, config){
25307     /**
25308     * The container element for this TabPanel.
25309     * @type Roo.Element
25310     */
25311     this.el = Roo.get(container, true);
25312     if(config){
25313         if(typeof config == "boolean"){
25314             this.tabPosition = config ? "bottom" : "top";
25315         }else{
25316             Roo.apply(this, config);
25317         }
25318     }
25319     if(this.tabPosition == "bottom"){
25320         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25321         this.el.addClass("x-tabs-bottom");
25322     }
25323     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25324     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25325     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25326     if(Roo.isIE){
25327         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25328     }
25329     if(this.tabPosition != "bottom"){
25330         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25331          * @type Roo.Element
25332          */
25333         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25334         this.el.addClass("x-tabs-top");
25335     }
25336     this.items = [];
25337
25338     this.bodyEl.setStyle("position", "relative");
25339
25340     this.active = null;
25341     this.activateDelegate = this.activate.createDelegate(this);
25342
25343     this.addEvents({
25344         /**
25345          * @event tabchange
25346          * Fires when the active tab changes
25347          * @param {Roo.TabPanel} this
25348          * @param {Roo.TabPanelItem} activePanel The new active tab
25349          */
25350         "tabchange": true,
25351         /**
25352          * @event beforetabchange
25353          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25354          * @param {Roo.TabPanel} this
25355          * @param {Object} e Set cancel to true on this object to cancel the tab change
25356          * @param {Roo.TabPanelItem} tab The tab being changed to
25357          */
25358         "beforetabchange" : true
25359     });
25360
25361     Roo.EventManager.onWindowResize(this.onResize, this);
25362     this.cpad = this.el.getPadding("lr");
25363     this.hiddenCount = 0;
25364
25365
25366     // toolbar on the tabbar support...
25367     if (this.toolbar) {
25368         var tcfg = this.toolbar;
25369         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25370         this.toolbar = new Roo.Toolbar(tcfg);
25371         if (Roo.isSafari) {
25372             var tbl = tcfg.container.child('table', true);
25373             tbl.setAttribute('width', '100%');
25374         }
25375         
25376     }
25377    
25378
25379
25380     Roo.TabPanel.superclass.constructor.call(this);
25381 };
25382
25383 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25384     /*
25385      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25386      */
25387     tabPosition : "top",
25388     /*
25389      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25390      */
25391     currentTabWidth : 0,
25392     /*
25393      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25394      */
25395     minTabWidth : 40,
25396     /*
25397      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25398      */
25399     maxTabWidth : 250,
25400     /*
25401      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25402      */
25403     preferredTabWidth : 175,
25404     /*
25405      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25406      */
25407     resizeTabs : false,
25408     /*
25409      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25410      */
25411     monitorResize : true,
25412     /*
25413      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25414      */
25415     toolbar : false,
25416
25417     /**
25418      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25419      * @param {String} id The id of the div to use <b>or create</b>
25420      * @param {String} text The text for the tab
25421      * @param {String} content (optional) Content to put in the TabPanelItem body
25422      * @param {Boolean} closable (optional) True to create a close icon on the tab
25423      * @return {Roo.TabPanelItem} The created TabPanelItem
25424      */
25425     addTab : function(id, text, content, closable){
25426         var item = new Roo.TabPanelItem(this, id, text, closable);
25427         this.addTabItem(item);
25428         if(content){
25429             item.setContent(content);
25430         }
25431         return item;
25432     },
25433
25434     /**
25435      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25436      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25437      * @return {Roo.TabPanelItem}
25438      */
25439     getTab : function(id){
25440         return this.items[id];
25441     },
25442
25443     /**
25444      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25445      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25446      */
25447     hideTab : function(id){
25448         var t = this.items[id];
25449         if(!t.isHidden()){
25450            t.setHidden(true);
25451            this.hiddenCount++;
25452            this.autoSizeTabs();
25453         }
25454     },
25455
25456     /**
25457      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25458      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25459      */
25460     unhideTab : function(id){
25461         var t = this.items[id];
25462         if(t.isHidden()){
25463            t.setHidden(false);
25464            this.hiddenCount--;
25465            this.autoSizeTabs();
25466         }
25467     },
25468
25469     /**
25470      * Adds an existing {@link Roo.TabPanelItem}.
25471      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25472      */
25473     addTabItem : function(item){
25474         this.items[item.id] = item;
25475         this.items.push(item);
25476         if(this.resizeTabs){
25477            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25478            this.autoSizeTabs();
25479         }else{
25480             item.autoSize();
25481         }
25482     },
25483
25484     /**
25485      * Removes a {@link Roo.TabPanelItem}.
25486      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25487      */
25488     removeTab : function(id){
25489         var items = this.items;
25490         var tab = items[id];
25491         if(!tab) { return; }
25492         var index = items.indexOf(tab);
25493         if(this.active == tab && items.length > 1){
25494             var newTab = this.getNextAvailable(index);
25495             if(newTab) {
25496                 newTab.activate();
25497             }
25498         }
25499         this.stripEl.dom.removeChild(tab.pnode.dom);
25500         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25501             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25502         }
25503         items.splice(index, 1);
25504         delete this.items[tab.id];
25505         tab.fireEvent("close", tab);
25506         tab.purgeListeners();
25507         this.autoSizeTabs();
25508     },
25509
25510     getNextAvailable : function(start){
25511         var items = this.items;
25512         var index = start;
25513         // look for a next tab that will slide over to
25514         // replace the one being removed
25515         while(index < items.length){
25516             var item = items[++index];
25517             if(item && !item.isHidden()){
25518                 return item;
25519             }
25520         }
25521         // if one isn't found select the previous tab (on the left)
25522         index = start;
25523         while(index >= 0){
25524             var item = items[--index];
25525             if(item && !item.isHidden()){
25526                 return item;
25527             }
25528         }
25529         return null;
25530     },
25531
25532     /**
25533      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25534      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25535      */
25536     disableTab : function(id){
25537         var tab = this.items[id];
25538         if(tab && this.active != tab){
25539             tab.disable();
25540         }
25541     },
25542
25543     /**
25544      * Enables a {@link Roo.TabPanelItem} that is disabled.
25545      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25546      */
25547     enableTab : function(id){
25548         var tab = this.items[id];
25549         tab.enable();
25550     },
25551
25552     /**
25553      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25554      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25555      * @return {Roo.TabPanelItem} The TabPanelItem.
25556      */
25557     activate : function(id){
25558         var tab = this.items[id];
25559         if(!tab){
25560             return null;
25561         }
25562         if(tab == this.active || tab.disabled){
25563             return tab;
25564         }
25565         var e = {};
25566         this.fireEvent("beforetabchange", this, e, tab);
25567         if(e.cancel !== true && !tab.disabled){
25568             if(this.active){
25569                 this.active.hide();
25570             }
25571             this.active = this.items[id];
25572             this.active.show();
25573             this.fireEvent("tabchange", this, this.active);
25574         }
25575         return tab;
25576     },
25577
25578     /**
25579      * Gets the active {@link Roo.TabPanelItem}.
25580      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25581      */
25582     getActiveTab : function(){
25583         return this.active;
25584     },
25585
25586     /**
25587      * Updates the tab body element to fit the height of the container element
25588      * for overflow scrolling
25589      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25590      */
25591     syncHeight : function(targetHeight){
25592         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25593         var bm = this.bodyEl.getMargins();
25594         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25595         this.bodyEl.setHeight(newHeight);
25596         return newHeight;
25597     },
25598
25599     onResize : function(){
25600         if(this.monitorResize){
25601             this.autoSizeTabs();
25602         }
25603     },
25604
25605     /**
25606      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25607      */
25608     beginUpdate : function(){
25609         this.updating = true;
25610     },
25611
25612     /**
25613      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25614      */
25615     endUpdate : function(){
25616         this.updating = false;
25617         this.autoSizeTabs();
25618     },
25619
25620     /**
25621      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25622      */
25623     autoSizeTabs : function(){
25624         var count = this.items.length;
25625         var vcount = count - this.hiddenCount;
25626         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25627         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25628         var availWidth = Math.floor(w / vcount);
25629         var b = this.stripBody;
25630         if(b.getWidth() > w){
25631             var tabs = this.items;
25632             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25633             if(availWidth < this.minTabWidth){
25634                 /*if(!this.sleft){    // incomplete scrolling code
25635                     this.createScrollButtons();
25636                 }
25637                 this.showScroll();
25638                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25639             }
25640         }else{
25641             if(this.currentTabWidth < this.preferredTabWidth){
25642                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25643             }
25644         }
25645     },
25646
25647     /**
25648      * Returns the number of tabs in this TabPanel.
25649      * @return {Number}
25650      */
25651      getCount : function(){
25652          return this.items.length;
25653      },
25654
25655     /**
25656      * Resizes all the tabs to the passed width
25657      * @param {Number} The new width
25658      */
25659     setTabWidth : function(width){
25660         this.currentTabWidth = width;
25661         for(var i = 0, len = this.items.length; i < len; i++) {
25662                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25663         }
25664     },
25665
25666     /**
25667      * Destroys this TabPanel
25668      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25669      */
25670     destroy : function(removeEl){
25671         Roo.EventManager.removeResizeListener(this.onResize, this);
25672         for(var i = 0, len = this.items.length; i < len; i++){
25673             this.items[i].purgeListeners();
25674         }
25675         if(removeEl === true){
25676             this.el.update("");
25677             this.el.remove();
25678         }
25679     }
25680 });
25681
25682 /**
25683  * @class Roo.TabPanelItem
25684  * @extends Roo.util.Observable
25685  * Represents an individual item (tab plus body) in a TabPanel.
25686  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25687  * @param {String} id The id of this TabPanelItem
25688  * @param {String} text The text for the tab of this TabPanelItem
25689  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25690  */
25691 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25692     /**
25693      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25694      * @type Roo.TabPanel
25695      */
25696     this.tabPanel = tabPanel;
25697     /**
25698      * The id for this TabPanelItem
25699      * @type String
25700      */
25701     this.id = id;
25702     /** @private */
25703     this.disabled = false;
25704     /** @private */
25705     this.text = text;
25706     /** @private */
25707     this.loaded = false;
25708     this.closable = closable;
25709
25710     /**
25711      * The body element for this TabPanelItem.
25712      * @type Roo.Element
25713      */
25714     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25715     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25716     this.bodyEl.setStyle("display", "block");
25717     this.bodyEl.setStyle("zoom", "1");
25718     this.hideAction();
25719
25720     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25721     /** @private */
25722     this.el = Roo.get(els.el, true);
25723     this.inner = Roo.get(els.inner, true);
25724     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25725     this.pnode = Roo.get(els.el.parentNode, true);
25726     this.el.on("mousedown", this.onTabMouseDown, this);
25727     this.el.on("click", this.onTabClick, this);
25728     /** @private */
25729     if(closable){
25730         var c = Roo.get(els.close, true);
25731         c.dom.title = this.closeText;
25732         c.addClassOnOver("close-over");
25733         c.on("click", this.closeClick, this);
25734      }
25735
25736     this.addEvents({
25737          /**
25738          * @event activate
25739          * Fires when this tab becomes the active tab.
25740          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25741          * @param {Roo.TabPanelItem} this
25742          */
25743         "activate": true,
25744         /**
25745          * @event beforeclose
25746          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25747          * @param {Roo.TabPanelItem} this
25748          * @param {Object} e Set cancel to true on this object to cancel the close.
25749          */
25750         "beforeclose": true,
25751         /**
25752          * @event close
25753          * Fires when this tab is closed.
25754          * @param {Roo.TabPanelItem} this
25755          */
25756          "close": true,
25757         /**
25758          * @event deactivate
25759          * Fires when this tab is no longer the active tab.
25760          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25761          * @param {Roo.TabPanelItem} this
25762          */
25763          "deactivate" : true
25764     });
25765     this.hidden = false;
25766
25767     Roo.TabPanelItem.superclass.constructor.call(this);
25768 };
25769
25770 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25771     purgeListeners : function(){
25772        Roo.util.Observable.prototype.purgeListeners.call(this);
25773        this.el.removeAllListeners();
25774     },
25775     /**
25776      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25777      */
25778     show : function(){
25779         this.pnode.addClass("on");
25780         this.showAction();
25781         if(Roo.isOpera){
25782             this.tabPanel.stripWrap.repaint();
25783         }
25784         this.fireEvent("activate", this.tabPanel, this);
25785     },
25786
25787     /**
25788      * Returns true if this tab is the active tab.
25789      * @return {Boolean}
25790      */
25791     isActive : function(){
25792         return this.tabPanel.getActiveTab() == this;
25793     },
25794
25795     /**
25796      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25797      */
25798     hide : function(){
25799         this.pnode.removeClass("on");
25800         this.hideAction();
25801         this.fireEvent("deactivate", this.tabPanel, this);
25802     },
25803
25804     hideAction : function(){
25805         this.bodyEl.hide();
25806         this.bodyEl.setStyle("position", "absolute");
25807         this.bodyEl.setLeft("-20000px");
25808         this.bodyEl.setTop("-20000px");
25809     },
25810
25811     showAction : function(){
25812         this.bodyEl.setStyle("position", "relative");
25813         this.bodyEl.setTop("");
25814         this.bodyEl.setLeft("");
25815         this.bodyEl.show();
25816     },
25817
25818     /**
25819      * Set the tooltip for the tab.
25820      * @param {String} tooltip The tab's tooltip
25821      */
25822     setTooltip : function(text){
25823         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25824             this.textEl.dom.qtip = text;
25825             this.textEl.dom.removeAttribute('title');
25826         }else{
25827             this.textEl.dom.title = text;
25828         }
25829     },
25830
25831     onTabClick : function(e){
25832         e.preventDefault();
25833         this.tabPanel.activate(this.id);
25834     },
25835
25836     onTabMouseDown : function(e){
25837         e.preventDefault();
25838         this.tabPanel.activate(this.id);
25839     },
25840
25841     getWidth : function(){
25842         return this.inner.getWidth();
25843     },
25844
25845     setWidth : function(width){
25846         var iwidth = width - this.pnode.getPadding("lr");
25847         this.inner.setWidth(iwidth);
25848         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25849         this.pnode.setWidth(width);
25850     },
25851
25852     /**
25853      * Show or hide the tab
25854      * @param {Boolean} hidden True to hide or false to show.
25855      */
25856     setHidden : function(hidden){
25857         this.hidden = hidden;
25858         this.pnode.setStyle("display", hidden ? "none" : "");
25859     },
25860
25861     /**
25862      * Returns true if this tab is "hidden"
25863      * @return {Boolean}
25864      */
25865     isHidden : function(){
25866         return this.hidden;
25867     },
25868
25869     /**
25870      * Returns the text for this tab
25871      * @return {String}
25872      */
25873     getText : function(){
25874         return this.text;
25875     },
25876
25877     autoSize : function(){
25878         //this.el.beginMeasure();
25879         this.textEl.setWidth(1);
25880         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25881         //this.el.endMeasure();
25882     },
25883
25884     /**
25885      * Sets the text for the tab (Note: this also sets the tooltip text)
25886      * @param {String} text The tab's text and tooltip
25887      */
25888     setText : function(text){
25889         this.text = text;
25890         this.textEl.update(text);
25891         this.setTooltip(text);
25892         if(!this.tabPanel.resizeTabs){
25893             this.autoSize();
25894         }
25895     },
25896     /**
25897      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25898      */
25899     activate : function(){
25900         this.tabPanel.activate(this.id);
25901     },
25902
25903     /**
25904      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25905      */
25906     disable : function(){
25907         if(this.tabPanel.active != this){
25908             this.disabled = true;
25909             this.pnode.addClass("disabled");
25910         }
25911     },
25912
25913     /**
25914      * Enables this TabPanelItem if it was previously disabled.
25915      */
25916     enable : function(){
25917         this.disabled = false;
25918         this.pnode.removeClass("disabled");
25919     },
25920
25921     /**
25922      * Sets the content for this TabPanelItem.
25923      * @param {String} content The content
25924      * @param {Boolean} loadScripts true to look for and load scripts
25925      */
25926     setContent : function(content, loadScripts){
25927         this.bodyEl.update(content, loadScripts);
25928     },
25929
25930     /**
25931      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25932      * @return {Roo.UpdateManager} The UpdateManager
25933      */
25934     getUpdateManager : function(){
25935         return this.bodyEl.getUpdateManager();
25936     },
25937
25938     /**
25939      * Set a URL to be used to load the content for this TabPanelItem.
25940      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25941      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
25942      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
25943      * @return {Roo.UpdateManager} The UpdateManager
25944      */
25945     setUrl : function(url, params, loadOnce){
25946         if(this.refreshDelegate){
25947             this.un('activate', this.refreshDelegate);
25948         }
25949         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25950         this.on("activate", this.refreshDelegate);
25951         return this.bodyEl.getUpdateManager();
25952     },
25953
25954     /** @private */
25955     _handleRefresh : function(url, params, loadOnce){
25956         if(!loadOnce || !this.loaded){
25957             var updater = this.bodyEl.getUpdateManager();
25958             updater.update(url, params, this._setLoaded.createDelegate(this));
25959         }
25960     },
25961
25962     /**
25963      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25964      *   Will fail silently if the setUrl method has not been called.
25965      *   This does not activate the panel, just updates its content.
25966      */
25967     refresh : function(){
25968         if(this.refreshDelegate){
25969            this.loaded = false;
25970            this.refreshDelegate();
25971         }
25972     },
25973
25974     /** @private */
25975     _setLoaded : function(){
25976         this.loaded = true;
25977     },
25978
25979     /** @private */
25980     closeClick : function(e){
25981         var o = {};
25982         e.stopEvent();
25983         this.fireEvent("beforeclose", this, o);
25984         if(o.cancel !== true){
25985             this.tabPanel.removeTab(this.id);
25986         }
25987     },
25988     /**
25989      * The text displayed in the tooltip for the close icon.
25990      * @type String
25991      */
25992     closeText : "Close this tab"
25993 });
25994
25995 /** @private */
25996 Roo.TabPanel.prototype.createStrip = function(container){
25997     var strip = document.createElement("div");
25998     strip.className = "x-tabs-wrap";
25999     container.appendChild(strip);
26000     return strip;
26001 };
26002 /** @private */
26003 Roo.TabPanel.prototype.createStripList = function(strip){
26004     // div wrapper for retard IE
26005     // returns the "tr" element.
26006     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
26007         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
26008         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
26009     return strip.firstChild.firstChild.firstChild.firstChild;
26010 };
26011 /** @private */
26012 Roo.TabPanel.prototype.createBody = function(container){
26013     var body = document.createElement("div");
26014     Roo.id(body, "tab-body");
26015     Roo.fly(body).addClass("x-tabs-body");
26016     container.appendChild(body);
26017     return body;
26018 };
26019 /** @private */
26020 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26021     var body = Roo.getDom(id);
26022     if(!body){
26023         body = document.createElement("div");
26024         body.id = id;
26025     }
26026     Roo.fly(body).addClass("x-tabs-item-body");
26027     bodyEl.insertBefore(body, bodyEl.firstChild);
26028     return body;
26029 };
26030 /** @private */
26031 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26032     var td = document.createElement("td");
26033     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26034     //stripEl.appendChild(td);
26035     if(closable){
26036         td.className = "x-tabs-closable";
26037         if(!this.closeTpl){
26038             this.closeTpl = new Roo.Template(
26039                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26040                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26041                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26042             );
26043         }
26044         var el = this.closeTpl.overwrite(td, {"text": text});
26045         var close = el.getElementsByTagName("div")[0];
26046         var inner = el.getElementsByTagName("em")[0];
26047         return {"el": el, "close": close, "inner": inner};
26048     } else {
26049         if(!this.tabTpl){
26050             this.tabTpl = new Roo.Template(
26051                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26052                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26053             );
26054         }
26055         var el = this.tabTpl.overwrite(td, {"text": text});
26056         var inner = el.getElementsByTagName("em")[0];
26057         return {"el": el, "inner": inner};
26058     }
26059 };/*
26060  * Based on:
26061  * Ext JS Library 1.1.1
26062  * Copyright(c) 2006-2007, Ext JS, LLC.
26063  *
26064  * Originally Released Under LGPL - original licence link has changed is not relivant.
26065  *
26066  * Fork - LGPL
26067  * <script type="text/javascript">
26068  */
26069
26070 /**
26071  * @class Roo.Button
26072  * @extends Roo.util.Observable
26073  * Simple Button class
26074  * @cfg {String} text The button text
26075  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26076  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26077  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26078  * @cfg {Object} scope The scope of the handler
26079  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26080  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26081  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26082  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26083  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26084  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26085    applies if enableToggle = true)
26086  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26087  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26088   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26089  * @constructor
26090  * Create a new button
26091  * @param {Object} config The config object
26092  */
26093 Roo.Button = function(renderTo, config)
26094 {
26095     if (!config) {
26096         config = renderTo;
26097         renderTo = config.renderTo || false;
26098     }
26099     
26100     Roo.apply(this, config);
26101     this.addEvents({
26102         /**
26103              * @event click
26104              * Fires when this button is clicked
26105              * @param {Button} this
26106              * @param {EventObject} e The click event
26107              */
26108             "click" : true,
26109         /**
26110              * @event toggle
26111              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26112              * @param {Button} this
26113              * @param {Boolean} pressed
26114              */
26115             "toggle" : true,
26116         /**
26117              * @event mouseover
26118              * Fires when the mouse hovers over the button
26119              * @param {Button} this
26120              * @param {Event} e The event object
26121              */
26122         'mouseover' : true,
26123         /**
26124              * @event mouseout
26125              * Fires when the mouse exits the button
26126              * @param {Button} this
26127              * @param {Event} e The event object
26128              */
26129         'mouseout': true,
26130          /**
26131              * @event render
26132              * Fires when the button is rendered
26133              * @param {Button} this
26134              */
26135         'render': true
26136     });
26137     if(this.menu){
26138         this.menu = Roo.menu.MenuMgr.get(this.menu);
26139     }
26140     // register listeners first!!  - so render can be captured..
26141     Roo.util.Observable.call(this);
26142     if(renderTo){
26143         this.render(renderTo);
26144     }
26145     
26146   
26147 };
26148
26149 Roo.extend(Roo.Button, Roo.util.Observable, {
26150     /**
26151      * 
26152      */
26153     
26154     /**
26155      * Read-only. True if this button is hidden
26156      * @type Boolean
26157      */
26158     hidden : false,
26159     /**
26160      * Read-only. True if this button is disabled
26161      * @type Boolean
26162      */
26163     disabled : false,
26164     /**
26165      * Read-only. True if this button is pressed (only if enableToggle = true)
26166      * @type Boolean
26167      */
26168     pressed : false,
26169
26170     /**
26171      * @cfg {Number} tabIndex 
26172      * The DOM tabIndex for this button (defaults to undefined)
26173      */
26174     tabIndex : undefined,
26175
26176     /**
26177      * @cfg {Boolean} enableToggle
26178      * True to enable pressed/not pressed toggling (defaults to false)
26179      */
26180     enableToggle: false,
26181     /**
26182      * @cfg {Mixed} menu
26183      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26184      */
26185     menu : undefined,
26186     /**
26187      * @cfg {String} menuAlign
26188      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26189      */
26190     menuAlign : "tl-bl?",
26191
26192     /**
26193      * @cfg {String} iconCls
26194      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26195      */
26196     iconCls : undefined,
26197     /**
26198      * @cfg {String} type
26199      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26200      */
26201     type : 'button',
26202
26203     // private
26204     menuClassTarget: 'tr',
26205
26206     /**
26207      * @cfg {String} clickEvent
26208      * The type of event to map to the button's event handler (defaults to 'click')
26209      */
26210     clickEvent : 'click',
26211
26212     /**
26213      * @cfg {Boolean} handleMouseEvents
26214      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26215      */
26216     handleMouseEvents : true,
26217
26218     /**
26219      * @cfg {String} tooltipType
26220      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26221      */
26222     tooltipType : 'qtip',
26223
26224     /**
26225      * @cfg {String} cls
26226      * A CSS class to apply to the button's main element.
26227      */
26228     
26229     /**
26230      * @cfg {Roo.Template} template (Optional)
26231      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26232      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26233      * require code modifications if required elements (e.g. a button) aren't present.
26234      */
26235
26236     // private
26237     render : function(renderTo){
26238         var btn;
26239         if(this.hideParent){
26240             this.parentEl = Roo.get(renderTo);
26241         }
26242         if(!this.dhconfig){
26243             if(!this.template){
26244                 if(!Roo.Button.buttonTemplate){
26245                     // hideous table template
26246                     Roo.Button.buttonTemplate = new Roo.Template(
26247                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26248                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
26249                         "</tr></tbody></table>");
26250                 }
26251                 this.template = Roo.Button.buttonTemplate;
26252             }
26253             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26254             var btnEl = btn.child("button:first");
26255             btnEl.on('focus', this.onFocus, this);
26256             btnEl.on('blur', this.onBlur, this);
26257             if(this.cls){
26258                 btn.addClass(this.cls);
26259             }
26260             if(this.icon){
26261                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26262             }
26263             if(this.iconCls){
26264                 btnEl.addClass(this.iconCls);
26265                 if(!this.cls){
26266                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26267                 }
26268             }
26269             if(this.tabIndex !== undefined){
26270                 btnEl.dom.tabIndex = this.tabIndex;
26271             }
26272             if(this.tooltip){
26273                 if(typeof this.tooltip == 'object'){
26274                     Roo.QuickTips.tips(Roo.apply({
26275                           target: btnEl.id
26276                     }, this.tooltip));
26277                 } else {
26278                     btnEl.dom[this.tooltipType] = this.tooltip;
26279                 }
26280             }
26281         }else{
26282             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26283         }
26284         this.el = btn;
26285         if(this.id){
26286             this.el.dom.id = this.el.id = this.id;
26287         }
26288         if(this.menu){
26289             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26290             this.menu.on("show", this.onMenuShow, this);
26291             this.menu.on("hide", this.onMenuHide, this);
26292         }
26293         btn.addClass("x-btn");
26294         if(Roo.isIE && !Roo.isIE7){
26295             this.autoWidth.defer(1, this);
26296         }else{
26297             this.autoWidth();
26298         }
26299         if(this.handleMouseEvents){
26300             btn.on("mouseover", this.onMouseOver, this);
26301             btn.on("mouseout", this.onMouseOut, this);
26302             btn.on("mousedown", this.onMouseDown, this);
26303         }
26304         btn.on(this.clickEvent, this.onClick, this);
26305         //btn.on("mouseup", this.onMouseUp, this);
26306         if(this.hidden){
26307             this.hide();
26308         }
26309         if(this.disabled){
26310             this.disable();
26311         }
26312         Roo.ButtonToggleMgr.register(this);
26313         if(this.pressed){
26314             this.el.addClass("x-btn-pressed");
26315         }
26316         if(this.repeat){
26317             var repeater = new Roo.util.ClickRepeater(btn,
26318                 typeof this.repeat == "object" ? this.repeat : {}
26319             );
26320             repeater.on("click", this.onClick,  this);
26321         }
26322         
26323         this.fireEvent('render', this);
26324         
26325     },
26326     /**
26327      * Returns the button's underlying element
26328      * @return {Roo.Element} The element
26329      */
26330     getEl : function(){
26331         return this.el;  
26332     },
26333     
26334     /**
26335      * Destroys this Button and removes any listeners.
26336      */
26337     destroy : function(){
26338         Roo.ButtonToggleMgr.unregister(this);
26339         this.el.removeAllListeners();
26340         this.purgeListeners();
26341         this.el.remove();
26342     },
26343
26344     // private
26345     autoWidth : function(){
26346         if(this.el){
26347             this.el.setWidth("auto");
26348             if(Roo.isIE7 && Roo.isStrict){
26349                 var ib = this.el.child('button');
26350                 if(ib && ib.getWidth() > 20){
26351                     ib.clip();
26352                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26353                 }
26354             }
26355             if(this.minWidth){
26356                 if(this.hidden){
26357                     this.el.beginMeasure();
26358                 }
26359                 if(this.el.getWidth() < this.minWidth){
26360                     this.el.setWidth(this.minWidth);
26361                 }
26362                 if(this.hidden){
26363                     this.el.endMeasure();
26364                 }
26365             }
26366         }
26367     },
26368
26369     /**
26370      * Assigns this button's click handler
26371      * @param {Function} handler The function to call when the button is clicked
26372      * @param {Object} scope (optional) Scope for the function passed in
26373      */
26374     setHandler : function(handler, scope){
26375         this.handler = handler;
26376         this.scope = scope;  
26377     },
26378     
26379     /**
26380      * Sets this button's text
26381      * @param {String} text The button text
26382      */
26383     setText : function(text){
26384         this.text = text;
26385         if(this.el){
26386             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26387         }
26388         this.autoWidth();
26389     },
26390     
26391     /**
26392      * Gets the text for this button
26393      * @return {String} The button text
26394      */
26395     getText : function(){
26396         return this.text;  
26397     },
26398     
26399     /**
26400      * Show this button
26401      */
26402     show: function(){
26403         this.hidden = false;
26404         if(this.el){
26405             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26406         }
26407     },
26408     
26409     /**
26410      * Hide this button
26411      */
26412     hide: function(){
26413         this.hidden = true;
26414         if(this.el){
26415             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26416         }
26417     },
26418     
26419     /**
26420      * Convenience function for boolean show/hide
26421      * @param {Boolean} visible True to show, false to hide
26422      */
26423     setVisible: function(visible){
26424         if(visible) {
26425             this.show();
26426         }else{
26427             this.hide();
26428         }
26429     },
26430     
26431     /**
26432      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26433      * @param {Boolean} state (optional) Force a particular state
26434      */
26435     toggle : function(state){
26436         state = state === undefined ? !this.pressed : state;
26437         if(state != this.pressed){
26438             if(state){
26439                 this.el.addClass("x-btn-pressed");
26440                 this.pressed = true;
26441                 this.fireEvent("toggle", this, true);
26442             }else{
26443                 this.el.removeClass("x-btn-pressed");
26444                 this.pressed = false;
26445                 this.fireEvent("toggle", this, false);
26446             }
26447             if(this.toggleHandler){
26448                 this.toggleHandler.call(this.scope || this, this, state);
26449             }
26450         }
26451     },
26452     
26453     /**
26454      * Focus the button
26455      */
26456     focus : function(){
26457         this.el.child('button:first').focus();
26458     },
26459     
26460     /**
26461      * Disable this button
26462      */
26463     disable : function(){
26464         if(this.el){
26465             this.el.addClass("x-btn-disabled");
26466         }
26467         this.disabled = true;
26468     },
26469     
26470     /**
26471      * Enable this button
26472      */
26473     enable : function(){
26474         if(this.el){
26475             this.el.removeClass("x-btn-disabled");
26476         }
26477         this.disabled = false;
26478     },
26479
26480     /**
26481      * Convenience function for boolean enable/disable
26482      * @param {Boolean} enabled True to enable, false to disable
26483      */
26484     setDisabled : function(v){
26485         this[v !== true ? "enable" : "disable"]();
26486     },
26487
26488     // private
26489     onClick : function(e){
26490         if(e){
26491             e.preventDefault();
26492         }
26493         if(e.button != 0){
26494             return;
26495         }
26496         if(!this.disabled){
26497             if(this.enableToggle){
26498                 this.toggle();
26499             }
26500             if(this.menu && !this.menu.isVisible()){
26501                 this.menu.show(this.el, this.menuAlign);
26502             }
26503             this.fireEvent("click", this, e);
26504             if(this.handler){
26505                 this.el.removeClass("x-btn-over");
26506                 this.handler.call(this.scope || this, this, e);
26507             }
26508         }
26509     },
26510     // private
26511     onMouseOver : function(e){
26512         if(!this.disabled){
26513             this.el.addClass("x-btn-over");
26514             this.fireEvent('mouseover', this, e);
26515         }
26516     },
26517     // private
26518     onMouseOut : function(e){
26519         if(!e.within(this.el,  true)){
26520             this.el.removeClass("x-btn-over");
26521             this.fireEvent('mouseout', this, e);
26522         }
26523     },
26524     // private
26525     onFocus : function(e){
26526         if(!this.disabled){
26527             this.el.addClass("x-btn-focus");
26528         }
26529     },
26530     // private
26531     onBlur : function(e){
26532         this.el.removeClass("x-btn-focus");
26533     },
26534     // private
26535     onMouseDown : function(e){
26536         if(!this.disabled && e.button == 0){
26537             this.el.addClass("x-btn-click");
26538             Roo.get(document).on('mouseup', this.onMouseUp, this);
26539         }
26540     },
26541     // private
26542     onMouseUp : function(e){
26543         if(e.button == 0){
26544             this.el.removeClass("x-btn-click");
26545             Roo.get(document).un('mouseup', this.onMouseUp, this);
26546         }
26547     },
26548     // private
26549     onMenuShow : function(e){
26550         this.el.addClass("x-btn-menu-active");
26551     },
26552     // private
26553     onMenuHide : function(e){
26554         this.el.removeClass("x-btn-menu-active");
26555     }   
26556 });
26557
26558 // Private utility class used by Button
26559 Roo.ButtonToggleMgr = function(){
26560    var groups = {};
26561    
26562    function toggleGroup(btn, state){
26563        if(state){
26564            var g = groups[btn.toggleGroup];
26565            for(var i = 0, l = g.length; i < l; i++){
26566                if(g[i] != btn){
26567                    g[i].toggle(false);
26568                }
26569            }
26570        }
26571    }
26572    
26573    return {
26574        register : function(btn){
26575            if(!btn.toggleGroup){
26576                return;
26577            }
26578            var g = groups[btn.toggleGroup];
26579            if(!g){
26580                g = groups[btn.toggleGroup] = [];
26581            }
26582            g.push(btn);
26583            btn.on("toggle", toggleGroup);
26584        },
26585        
26586        unregister : function(btn){
26587            if(!btn.toggleGroup){
26588                return;
26589            }
26590            var g = groups[btn.toggleGroup];
26591            if(g){
26592                g.remove(btn);
26593                btn.un("toggle", toggleGroup);
26594            }
26595        }
26596    };
26597 }();/*
26598  * Based on:
26599  * Ext JS Library 1.1.1
26600  * Copyright(c) 2006-2007, Ext JS, LLC.
26601  *
26602  * Originally Released Under LGPL - original licence link has changed is not relivant.
26603  *
26604  * Fork - LGPL
26605  * <script type="text/javascript">
26606  */
26607  
26608 /**
26609  * @class Roo.SplitButton
26610  * @extends Roo.Button
26611  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26612  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26613  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26614  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26615  * @cfg {String} arrowTooltip The title attribute of the arrow
26616  * @constructor
26617  * Create a new menu button
26618  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26619  * @param {Object} config The config object
26620  */
26621 Roo.SplitButton = function(renderTo, config){
26622     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26623     /**
26624      * @event arrowclick
26625      * Fires when this button's arrow is clicked
26626      * @param {SplitButton} this
26627      * @param {EventObject} e The click event
26628      */
26629     this.addEvents({"arrowclick":true});
26630 };
26631
26632 Roo.extend(Roo.SplitButton, Roo.Button, {
26633     render : function(renderTo){
26634         // this is one sweet looking template!
26635         var tpl = new Roo.Template(
26636             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26637             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26638             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
26639             "</tbody></table></td><td>",
26640             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26641             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
26642             "</tbody></table></td></tr></table>"
26643         );
26644         var btn = tpl.append(renderTo, [this.text, this.type], true);
26645         var btnEl = btn.child("button");
26646         if(this.cls){
26647             btn.addClass(this.cls);
26648         }
26649         if(this.icon){
26650             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26651         }
26652         if(this.iconCls){
26653             btnEl.addClass(this.iconCls);
26654             if(!this.cls){
26655                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26656             }
26657         }
26658         this.el = btn;
26659         if(this.handleMouseEvents){
26660             btn.on("mouseover", this.onMouseOver, this);
26661             btn.on("mouseout", this.onMouseOut, this);
26662             btn.on("mousedown", this.onMouseDown, this);
26663             btn.on("mouseup", this.onMouseUp, this);
26664         }
26665         btn.on(this.clickEvent, this.onClick, this);
26666         if(this.tooltip){
26667             if(typeof this.tooltip == 'object'){
26668                 Roo.QuickTips.tips(Roo.apply({
26669                       target: btnEl.id
26670                 }, this.tooltip));
26671             } else {
26672                 btnEl.dom[this.tooltipType] = this.tooltip;
26673             }
26674         }
26675         if(this.arrowTooltip){
26676             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26677         }
26678         if(this.hidden){
26679             this.hide();
26680         }
26681         if(this.disabled){
26682             this.disable();
26683         }
26684         if(this.pressed){
26685             this.el.addClass("x-btn-pressed");
26686         }
26687         if(Roo.isIE && !Roo.isIE7){
26688             this.autoWidth.defer(1, this);
26689         }else{
26690             this.autoWidth();
26691         }
26692         if(this.menu){
26693             this.menu.on("show", this.onMenuShow, this);
26694             this.menu.on("hide", this.onMenuHide, this);
26695         }
26696         this.fireEvent('render', this);
26697     },
26698
26699     // private
26700     autoWidth : function(){
26701         if(this.el){
26702             var tbl = this.el.child("table:first");
26703             var tbl2 = this.el.child("table:last");
26704             this.el.setWidth("auto");
26705             tbl.setWidth("auto");
26706             if(Roo.isIE7 && Roo.isStrict){
26707                 var ib = this.el.child('button:first');
26708                 if(ib && ib.getWidth() > 20){
26709                     ib.clip();
26710                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26711                 }
26712             }
26713             if(this.minWidth){
26714                 if(this.hidden){
26715                     this.el.beginMeasure();
26716                 }
26717                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26718                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26719                 }
26720                 if(this.hidden){
26721                     this.el.endMeasure();
26722                 }
26723             }
26724             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26725         } 
26726     },
26727     /**
26728      * Sets this button's click handler
26729      * @param {Function} handler The function to call when the button is clicked
26730      * @param {Object} scope (optional) Scope for the function passed above
26731      */
26732     setHandler : function(handler, scope){
26733         this.handler = handler;
26734         this.scope = scope;  
26735     },
26736     
26737     /**
26738      * Sets this button's arrow click handler
26739      * @param {Function} handler The function to call when the arrow is clicked
26740      * @param {Object} scope (optional) Scope for the function passed above
26741      */
26742     setArrowHandler : function(handler, scope){
26743         this.arrowHandler = handler;
26744         this.scope = scope;  
26745     },
26746     
26747     /**
26748      * Focus the button
26749      */
26750     focus : function(){
26751         if(this.el){
26752             this.el.child("button:first").focus();
26753         }
26754     },
26755
26756     // private
26757     onClick : function(e){
26758         e.preventDefault();
26759         if(!this.disabled){
26760             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26761                 if(this.menu && !this.menu.isVisible()){
26762                     this.menu.show(this.el, this.menuAlign);
26763                 }
26764                 this.fireEvent("arrowclick", this, e);
26765                 if(this.arrowHandler){
26766                     this.arrowHandler.call(this.scope || this, this, e);
26767                 }
26768             }else{
26769                 this.fireEvent("click", this, e);
26770                 if(this.handler){
26771                     this.handler.call(this.scope || this, this, e);
26772                 }
26773             }
26774         }
26775     },
26776     // private
26777     onMouseDown : function(e){
26778         if(!this.disabled){
26779             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26780         }
26781     },
26782     // private
26783     onMouseUp : function(e){
26784         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26785     }   
26786 });
26787
26788
26789 // backwards compat
26790 Roo.MenuButton = Roo.SplitButton;/*
26791  * Based on:
26792  * Ext JS Library 1.1.1
26793  * Copyright(c) 2006-2007, Ext JS, LLC.
26794  *
26795  * Originally Released Under LGPL - original licence link has changed is not relivant.
26796  *
26797  * Fork - LGPL
26798  * <script type="text/javascript">
26799  */
26800
26801 /**
26802  * @class Roo.Toolbar
26803  * Basic Toolbar class.
26804  * @constructor
26805  * Creates a new Toolbar
26806  * @param {Object} container The config object
26807  */ 
26808 Roo.Toolbar = function(container, buttons, config)
26809 {
26810     /// old consturctor format still supported..
26811     if(container instanceof Array){ // omit the container for later rendering
26812         buttons = container;
26813         config = buttons;
26814         container = null;
26815     }
26816     if (typeof(container) == 'object' && container.xtype) {
26817         config = container;
26818         container = config.container;
26819         buttons = config.buttons || []; // not really - use items!!
26820     }
26821     var xitems = [];
26822     if (config && config.items) {
26823         xitems = config.items;
26824         delete config.items;
26825     }
26826     Roo.apply(this, config);
26827     this.buttons = buttons;
26828     
26829     if(container){
26830         this.render(container);
26831     }
26832     this.xitems = xitems;
26833     Roo.each(xitems, function(b) {
26834         this.add(b);
26835     }, this);
26836     
26837 };
26838
26839 Roo.Toolbar.prototype = {
26840     /**
26841      * @cfg {Array} items
26842      * array of button configs or elements to add (will be converted to a MixedCollection)
26843      */
26844     
26845     /**
26846      * @cfg {String/HTMLElement/Element} container
26847      * The id or element that will contain the toolbar
26848      */
26849     // private
26850     render : function(ct){
26851         this.el = Roo.get(ct);
26852         if(this.cls){
26853             this.el.addClass(this.cls);
26854         }
26855         // using a table allows for vertical alignment
26856         // 100% width is needed by Safari...
26857         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26858         this.tr = this.el.child("tr", true);
26859         var autoId = 0;
26860         this.items = new Roo.util.MixedCollection(false, function(o){
26861             return o.id || ("item" + (++autoId));
26862         });
26863         if(this.buttons){
26864             this.add.apply(this, this.buttons);
26865             delete this.buttons;
26866         }
26867     },
26868
26869     /**
26870      * Adds element(s) to the toolbar -- this function takes a variable number of 
26871      * arguments of mixed type and adds them to the toolbar.
26872      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26873      * <ul>
26874      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26875      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26876      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26877      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26878      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26879      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26880      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26881      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26882      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26883      * </ul>
26884      * @param {Mixed} arg2
26885      * @param {Mixed} etc.
26886      */
26887     add : function(){
26888         var a = arguments, l = a.length;
26889         for(var i = 0; i < l; i++){
26890             this._add(a[i]);
26891         }
26892     },
26893     // private..
26894     _add : function(el) {
26895         
26896         if (el.xtype) {
26897             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26898         }
26899         
26900         if (el.applyTo){ // some kind of form field
26901             return this.addField(el);
26902         } 
26903         if (el.render){ // some kind of Toolbar.Item
26904             return this.addItem(el);
26905         }
26906         if (typeof el == "string"){ // string
26907             if(el == "separator" || el == "-"){
26908                 return this.addSeparator();
26909             }
26910             if (el == " "){
26911                 return this.addSpacer();
26912             }
26913             if(el == "->"){
26914                 return this.addFill();
26915             }
26916             return this.addText(el);
26917             
26918         }
26919         if(el.tagName){ // element
26920             return this.addElement(el);
26921         }
26922         if(typeof el == "object"){ // must be button config?
26923             return this.addButton(el);
26924         }
26925         // and now what?!?!
26926         return false;
26927         
26928     },
26929     
26930     /**
26931      * Add an Xtype element
26932      * @param {Object} xtype Xtype Object
26933      * @return {Object} created Object
26934      */
26935     addxtype : function(e){
26936         return this.add(e);  
26937     },
26938     
26939     /**
26940      * Returns the Element for this toolbar.
26941      * @return {Roo.Element}
26942      */
26943     getEl : function(){
26944         return this.el;  
26945     },
26946     
26947     /**
26948      * Adds a separator
26949      * @return {Roo.Toolbar.Item} The separator item
26950      */
26951     addSeparator : function(){
26952         return this.addItem(new Roo.Toolbar.Separator());
26953     },
26954
26955     /**
26956      * Adds a spacer element
26957      * @return {Roo.Toolbar.Spacer} The spacer item
26958      */
26959     addSpacer : function(){
26960         return this.addItem(new Roo.Toolbar.Spacer());
26961     },
26962
26963     /**
26964      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26965      * @return {Roo.Toolbar.Fill} The fill item
26966      */
26967     addFill : function(){
26968         return this.addItem(new Roo.Toolbar.Fill());
26969     },
26970
26971     /**
26972      * Adds any standard HTML element to the toolbar
26973      * @param {String/HTMLElement/Element} el The element or id of the element to add
26974      * @return {Roo.Toolbar.Item} The element's item
26975      */
26976     addElement : function(el){
26977         return this.addItem(new Roo.Toolbar.Item(el));
26978     },
26979     /**
26980      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26981      * @type Roo.util.MixedCollection  
26982      */
26983     items : false,
26984      
26985     /**
26986      * Adds any Toolbar.Item or subclass
26987      * @param {Roo.Toolbar.Item} item
26988      * @return {Roo.Toolbar.Item} The item
26989      */
26990     addItem : function(item){
26991         var td = this.nextBlock();
26992         item.render(td);
26993         this.items.add(item);
26994         return item;
26995     },
26996     
26997     /**
26998      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26999      * @param {Object/Array} config A button config or array of configs
27000      * @return {Roo.Toolbar.Button/Array}
27001      */
27002     addButton : function(config){
27003         if(config instanceof Array){
27004             var buttons = [];
27005             for(var i = 0, len = config.length; i < len; i++) {
27006                 buttons.push(this.addButton(config[i]));
27007             }
27008             return buttons;
27009         }
27010         var b = config;
27011         if(!(config instanceof Roo.Toolbar.Button)){
27012             b = config.split ?
27013                 new Roo.Toolbar.SplitButton(config) :
27014                 new Roo.Toolbar.Button(config);
27015         }
27016         var td = this.nextBlock();
27017         b.render(td);
27018         this.items.add(b);
27019         return b;
27020     },
27021     
27022     /**
27023      * Adds text to the toolbar
27024      * @param {String} text The text to add
27025      * @return {Roo.Toolbar.Item} The element's item
27026      */
27027     addText : function(text){
27028         return this.addItem(new Roo.Toolbar.TextItem(text));
27029     },
27030     
27031     /**
27032      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27033      * @param {Number} index The index where the item is to be inserted
27034      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27035      * @return {Roo.Toolbar.Button/Item}
27036      */
27037     insertButton : function(index, item){
27038         if(item instanceof Array){
27039             var buttons = [];
27040             for(var i = 0, len = item.length; i < len; i++) {
27041                buttons.push(this.insertButton(index + i, item[i]));
27042             }
27043             return buttons;
27044         }
27045         if (!(item instanceof Roo.Toolbar.Button)){
27046            item = new Roo.Toolbar.Button(item);
27047         }
27048         var td = document.createElement("td");
27049         this.tr.insertBefore(td, this.tr.childNodes[index]);
27050         item.render(td);
27051         this.items.insert(index, item);
27052         return item;
27053     },
27054     
27055     /**
27056      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27057      * @param {Object} config
27058      * @return {Roo.Toolbar.Item} The element's item
27059      */
27060     addDom : function(config, returnEl){
27061         var td = this.nextBlock();
27062         Roo.DomHelper.overwrite(td, config);
27063         var ti = new Roo.Toolbar.Item(td.firstChild);
27064         ti.render(td);
27065         this.items.add(ti);
27066         return ti;
27067     },
27068
27069     /**
27070      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27071      * @type Roo.util.MixedCollection  
27072      */
27073     fields : false,
27074     
27075     /**
27076      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27077      * Note: the field should not have been rendered yet. For a field that has already been
27078      * rendered, use {@link #addElement}.
27079      * @param {Roo.form.Field} field
27080      * @return {Roo.ToolbarItem}
27081      */
27082      
27083       
27084     addField : function(field) {
27085         if (!this.fields) {
27086             var autoId = 0;
27087             this.fields = new Roo.util.MixedCollection(false, function(o){
27088                 return o.id || ("item" + (++autoId));
27089             });
27090
27091         }
27092         
27093         var td = this.nextBlock();
27094         field.render(td);
27095         var ti = new Roo.Toolbar.Item(td.firstChild);
27096         ti.render(td);
27097         this.items.add(ti);
27098         this.fields.add(field);
27099         return ti;
27100     },
27101     /**
27102      * Hide the toolbar
27103      * @method hide
27104      */
27105      
27106       
27107     hide : function()
27108     {
27109         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27110         this.el.child('div').hide();
27111     },
27112     /**
27113      * Show the toolbar
27114      * @method show
27115      */
27116     show : function()
27117     {
27118         this.el.child('div').show();
27119     },
27120       
27121     // private
27122     nextBlock : function(){
27123         var td = document.createElement("td");
27124         this.tr.appendChild(td);
27125         return td;
27126     },
27127
27128     // private
27129     destroy : function(){
27130         if(this.items){ // rendered?
27131             Roo.destroy.apply(Roo, this.items.items);
27132         }
27133         if(this.fields){ // rendered?
27134             Roo.destroy.apply(Roo, this.fields.items);
27135         }
27136         Roo.Element.uncache(this.el, this.tr);
27137     }
27138 };
27139
27140 /**
27141  * @class Roo.Toolbar.Item
27142  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27143  * @constructor
27144  * Creates a new Item
27145  * @param {HTMLElement} el 
27146  */
27147 Roo.Toolbar.Item = function(el){
27148     this.el = Roo.getDom(el);
27149     this.id = Roo.id(this.el);
27150     this.hidden = false;
27151 };
27152
27153 Roo.Toolbar.Item.prototype = {
27154     
27155     /**
27156      * Get this item's HTML Element
27157      * @return {HTMLElement}
27158      */
27159     getEl : function(){
27160        return this.el;  
27161     },
27162
27163     // private
27164     render : function(td){
27165         this.td = td;
27166         td.appendChild(this.el);
27167     },
27168     
27169     /**
27170      * Removes and destroys this item.
27171      */
27172     destroy : function(){
27173         this.td.parentNode.removeChild(this.td);
27174     },
27175     
27176     /**
27177      * Shows this item.
27178      */
27179     show: function(){
27180         this.hidden = false;
27181         this.td.style.display = "";
27182     },
27183     
27184     /**
27185      * Hides this item.
27186      */
27187     hide: function(){
27188         this.hidden = true;
27189         this.td.style.display = "none";
27190     },
27191     
27192     /**
27193      * Convenience function for boolean show/hide.
27194      * @param {Boolean} visible true to show/false to hide
27195      */
27196     setVisible: function(visible){
27197         if(visible) {
27198             this.show();
27199         }else{
27200             this.hide();
27201         }
27202     },
27203     
27204     /**
27205      * Try to focus this item.
27206      */
27207     focus : function(){
27208         Roo.fly(this.el).focus();
27209     },
27210     
27211     /**
27212      * Disables this item.
27213      */
27214     disable : function(){
27215         Roo.fly(this.td).addClass("x-item-disabled");
27216         this.disabled = true;
27217         this.el.disabled = true;
27218     },
27219     
27220     /**
27221      * Enables this item.
27222      */
27223     enable : function(){
27224         Roo.fly(this.td).removeClass("x-item-disabled");
27225         this.disabled = false;
27226         this.el.disabled = false;
27227     }
27228 };
27229
27230
27231 /**
27232  * @class Roo.Toolbar.Separator
27233  * @extends Roo.Toolbar.Item
27234  * A simple toolbar separator class
27235  * @constructor
27236  * Creates a new Separator
27237  */
27238 Roo.Toolbar.Separator = function(){
27239     var s = document.createElement("span");
27240     s.className = "ytb-sep";
27241     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27242 };
27243 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27244     enable:Roo.emptyFn,
27245     disable:Roo.emptyFn,
27246     focus:Roo.emptyFn
27247 });
27248
27249 /**
27250  * @class Roo.Toolbar.Spacer
27251  * @extends Roo.Toolbar.Item
27252  * A simple element that adds extra horizontal space to a toolbar.
27253  * @constructor
27254  * Creates a new Spacer
27255  */
27256 Roo.Toolbar.Spacer = function(){
27257     var s = document.createElement("div");
27258     s.className = "ytb-spacer";
27259     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27260 };
27261 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27262     enable:Roo.emptyFn,
27263     disable:Roo.emptyFn,
27264     focus:Roo.emptyFn
27265 });
27266
27267 /**
27268  * @class Roo.Toolbar.Fill
27269  * @extends Roo.Toolbar.Spacer
27270  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27271  * @constructor
27272  * Creates a new Spacer
27273  */
27274 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27275     // private
27276     render : function(td){
27277         td.style.width = '100%';
27278         Roo.Toolbar.Fill.superclass.render.call(this, td);
27279     }
27280 });
27281
27282 /**
27283  * @class Roo.Toolbar.TextItem
27284  * @extends Roo.Toolbar.Item
27285  * A simple class that renders text directly into a toolbar.
27286  * @constructor
27287  * Creates a new TextItem
27288  * @param {String} text
27289  */
27290 Roo.Toolbar.TextItem = function(text){
27291     if (typeof(text) == 'object') {
27292         text = text.text;
27293     }
27294     var s = document.createElement("span");
27295     s.className = "ytb-text";
27296     s.innerHTML = text;
27297     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27298 };
27299 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27300     enable:Roo.emptyFn,
27301     disable:Roo.emptyFn,
27302     focus:Roo.emptyFn
27303 });
27304
27305 /**
27306  * @class Roo.Toolbar.Button
27307  * @extends Roo.Button
27308  * A button that renders into a toolbar.
27309  * @constructor
27310  * Creates a new Button
27311  * @param {Object} config A standard {@link Roo.Button} config object
27312  */
27313 Roo.Toolbar.Button = function(config){
27314     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27315 };
27316 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27317     render : function(td){
27318         this.td = td;
27319         Roo.Toolbar.Button.superclass.render.call(this, td);
27320     },
27321     
27322     /**
27323      * Removes and destroys this button
27324      */
27325     destroy : function(){
27326         Roo.Toolbar.Button.superclass.destroy.call(this);
27327         this.td.parentNode.removeChild(this.td);
27328     },
27329     
27330     /**
27331      * Shows this button
27332      */
27333     show: function(){
27334         this.hidden = false;
27335         this.td.style.display = "";
27336     },
27337     
27338     /**
27339      * Hides this button
27340      */
27341     hide: function(){
27342         this.hidden = true;
27343         this.td.style.display = "none";
27344     },
27345
27346     /**
27347      * Disables this item
27348      */
27349     disable : function(){
27350         Roo.fly(this.td).addClass("x-item-disabled");
27351         this.disabled = true;
27352     },
27353
27354     /**
27355      * Enables this item
27356      */
27357     enable : function(){
27358         Roo.fly(this.td).removeClass("x-item-disabled");
27359         this.disabled = false;
27360     }
27361 });
27362 // backwards compat
27363 Roo.ToolbarButton = Roo.Toolbar.Button;
27364
27365 /**
27366  * @class Roo.Toolbar.SplitButton
27367  * @extends Roo.SplitButton
27368  * A menu button that renders into a toolbar.
27369  * @constructor
27370  * Creates a new SplitButton
27371  * @param {Object} config A standard {@link Roo.SplitButton} config object
27372  */
27373 Roo.Toolbar.SplitButton = function(config){
27374     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27375 };
27376 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27377     render : function(td){
27378         this.td = td;
27379         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27380     },
27381     
27382     /**
27383      * Removes and destroys this button
27384      */
27385     destroy : function(){
27386         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27387         this.td.parentNode.removeChild(this.td);
27388     },
27389     
27390     /**
27391      * Shows this button
27392      */
27393     show: function(){
27394         this.hidden = false;
27395         this.td.style.display = "";
27396     },
27397     
27398     /**
27399      * Hides this button
27400      */
27401     hide: function(){
27402         this.hidden = true;
27403         this.td.style.display = "none";
27404     }
27405 });
27406
27407 // backwards compat
27408 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27409  * Based on:
27410  * Ext JS Library 1.1.1
27411  * Copyright(c) 2006-2007, Ext JS, LLC.
27412  *
27413  * Originally Released Under LGPL - original licence link has changed is not relivant.
27414  *
27415  * Fork - LGPL
27416  * <script type="text/javascript">
27417  */
27418  
27419 /**
27420  * @class Roo.PagingToolbar
27421  * @extends Roo.Toolbar
27422  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27423  * @constructor
27424  * Create a new PagingToolbar
27425  * @param {Object} config The config object
27426  */
27427 Roo.PagingToolbar = function(el, ds, config)
27428 {
27429     // old args format still supported... - xtype is prefered..
27430     if (typeof(el) == 'object' && el.xtype) {
27431         // created from xtype...
27432         config = el;
27433         ds = el.dataSource;
27434         el = config.container;
27435     }
27436     var items = [];
27437     if (config.items) {
27438         items = config.items;
27439         config.items = [];
27440     }
27441     
27442     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27443     this.ds = ds;
27444     this.cursor = 0;
27445     this.renderButtons(this.el);
27446     this.bind(ds);
27447     
27448     // supprot items array.
27449    
27450     Roo.each(items, function(e) {
27451         this.add(Roo.factory(e));
27452     },this);
27453     
27454 };
27455
27456 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27457     /**
27458      * @cfg {Roo.data.Store} dataSource
27459      * The underlying data store providing the paged data
27460      */
27461     /**
27462      * @cfg {String/HTMLElement/Element} container
27463      * container The id or element that will contain the toolbar
27464      */
27465     /**
27466      * @cfg {Boolean} displayInfo
27467      * True to display the displayMsg (defaults to false)
27468      */
27469     /**
27470      * @cfg {Number} pageSize
27471      * The number of records to display per page (defaults to 20)
27472      */
27473     pageSize: 20,
27474     /**
27475      * @cfg {String} displayMsg
27476      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27477      */
27478     displayMsg : 'Displaying {0} - {1} of {2}',
27479     /**
27480      * @cfg {String} emptyMsg
27481      * The message to display when no records are found (defaults to "No data to display")
27482      */
27483     emptyMsg : 'No data to display',
27484     /**
27485      * Customizable piece of the default paging text (defaults to "Page")
27486      * @type String
27487      */
27488     beforePageText : "Page",
27489     /**
27490      * Customizable piece of the default paging text (defaults to "of %0")
27491      * @type String
27492      */
27493     afterPageText : "of {0}",
27494     /**
27495      * Customizable piece of the default paging text (defaults to "First Page")
27496      * @type String
27497      */
27498     firstText : "First Page",
27499     /**
27500      * Customizable piece of the default paging text (defaults to "Previous Page")
27501      * @type String
27502      */
27503     prevText : "Previous Page",
27504     /**
27505      * Customizable piece of the default paging text (defaults to "Next Page")
27506      * @type String
27507      */
27508     nextText : "Next Page",
27509     /**
27510      * Customizable piece of the default paging text (defaults to "Last Page")
27511      * @type String
27512      */
27513     lastText : "Last Page",
27514     /**
27515      * Customizable piece of the default paging text (defaults to "Refresh")
27516      * @type String
27517      */
27518     refreshText : "Refresh",
27519
27520     // private
27521     renderButtons : function(el){
27522         Roo.PagingToolbar.superclass.render.call(this, el);
27523         this.first = this.addButton({
27524             tooltip: this.firstText,
27525             cls: "x-btn-icon x-grid-page-first",
27526             disabled: true,
27527             handler: this.onClick.createDelegate(this, ["first"])
27528         });
27529         this.prev = this.addButton({
27530             tooltip: this.prevText,
27531             cls: "x-btn-icon x-grid-page-prev",
27532             disabled: true,
27533             handler: this.onClick.createDelegate(this, ["prev"])
27534         });
27535         //this.addSeparator();
27536         this.add(this.beforePageText);
27537         this.field = Roo.get(this.addDom({
27538            tag: "input",
27539            type: "text",
27540            size: "3",
27541            value: "1",
27542            cls: "x-grid-page-number"
27543         }).el);
27544         this.field.on("keydown", this.onPagingKeydown, this);
27545         this.field.on("focus", function(){this.dom.select();});
27546         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27547         this.field.setHeight(18);
27548         //this.addSeparator();
27549         this.next = this.addButton({
27550             tooltip: this.nextText,
27551             cls: "x-btn-icon x-grid-page-next",
27552             disabled: true,
27553             handler: this.onClick.createDelegate(this, ["next"])
27554         });
27555         this.last = this.addButton({
27556             tooltip: this.lastText,
27557             cls: "x-btn-icon x-grid-page-last",
27558             disabled: true,
27559             handler: this.onClick.createDelegate(this, ["last"])
27560         });
27561         //this.addSeparator();
27562         this.loading = this.addButton({
27563             tooltip: this.refreshText,
27564             cls: "x-btn-icon x-grid-loading",
27565             handler: this.onClick.createDelegate(this, ["refresh"])
27566         });
27567
27568         if(this.displayInfo){
27569             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27570         }
27571     },
27572
27573     // private
27574     updateInfo : function(){
27575         if(this.displayEl){
27576             var count = this.ds.getCount();
27577             var msg = count == 0 ?
27578                 this.emptyMsg :
27579                 String.format(
27580                     this.displayMsg,
27581                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27582                 );
27583             this.displayEl.update(msg);
27584         }
27585     },
27586
27587     // private
27588     onLoad : function(ds, r, o){
27589        this.cursor = o.params ? o.params.start : 0;
27590        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27591
27592        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27593        this.field.dom.value = ap;
27594        this.first.setDisabled(ap == 1);
27595        this.prev.setDisabled(ap == 1);
27596        this.next.setDisabled(ap == ps);
27597        this.last.setDisabled(ap == ps);
27598        this.loading.enable();
27599        this.updateInfo();
27600     },
27601
27602     // private
27603     getPageData : function(){
27604         var total = this.ds.getTotalCount();
27605         return {
27606             total : total,
27607             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27608             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27609         };
27610     },
27611
27612     // private
27613     onLoadError : function(){
27614         this.loading.enable();
27615     },
27616
27617     // private
27618     onPagingKeydown : function(e){
27619         var k = e.getKey();
27620         var d = this.getPageData();
27621         if(k == e.RETURN){
27622             var v = this.field.dom.value, pageNum;
27623             if(!v || isNaN(pageNum = parseInt(v, 10))){
27624                 this.field.dom.value = d.activePage;
27625                 return;
27626             }
27627             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27628             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27629             e.stopEvent();
27630         }
27631         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
27632         {
27633           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27634           this.field.dom.value = pageNum;
27635           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27636           e.stopEvent();
27637         }
27638         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27639         {
27640           var v = this.field.dom.value, pageNum; 
27641           var increment = (e.shiftKey) ? 10 : 1;
27642           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27643             increment *= -1;
27644           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27645             this.field.dom.value = d.activePage;
27646             return;
27647           }
27648           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27649           {
27650             this.field.dom.value = parseInt(v, 10) + increment;
27651             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27652             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27653           }
27654           e.stopEvent();
27655         }
27656     },
27657
27658     // private
27659     beforeLoad : function(){
27660         if(this.loading){
27661             this.loading.disable();
27662         }
27663     },
27664
27665     // private
27666     onClick : function(which){
27667         var ds = this.ds;
27668         switch(which){
27669             case "first":
27670                 ds.load({params:{start: 0, limit: this.pageSize}});
27671             break;
27672             case "prev":
27673                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27674             break;
27675             case "next":
27676                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27677             break;
27678             case "last":
27679                 var total = ds.getTotalCount();
27680                 var extra = total % this.pageSize;
27681                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27682                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27683             break;
27684             case "refresh":
27685                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27686             break;
27687         }
27688     },
27689
27690     /**
27691      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27692      * @param {Roo.data.Store} store The data store to unbind
27693      */
27694     unbind : function(ds){
27695         ds.un("beforeload", this.beforeLoad, this);
27696         ds.un("load", this.onLoad, this);
27697         ds.un("loadexception", this.onLoadError, this);
27698         ds.un("remove", this.updateInfo, this);
27699         ds.un("add", this.updateInfo, this);
27700         this.ds = undefined;
27701     },
27702
27703     /**
27704      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27705      * @param {Roo.data.Store} store The data store to bind
27706      */
27707     bind : function(ds){
27708         ds.on("beforeload", this.beforeLoad, this);
27709         ds.on("load", this.onLoad, this);
27710         ds.on("loadexception", this.onLoadError, this);
27711         ds.on("remove", this.updateInfo, this);
27712         ds.on("add", this.updateInfo, this);
27713         this.ds = ds;
27714     }
27715 });/*
27716  * Based on:
27717  * Ext JS Library 1.1.1
27718  * Copyright(c) 2006-2007, Ext JS, LLC.
27719  *
27720  * Originally Released Under LGPL - original licence link has changed is not relivant.
27721  *
27722  * Fork - LGPL
27723  * <script type="text/javascript">
27724  */
27725
27726 /**
27727  * @class Roo.Resizable
27728  * @extends Roo.util.Observable
27729  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27730  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27731  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
27732  * the element will be wrapped for you automatically.</p>
27733  * <p>Here is the list of valid resize handles:</p>
27734  * <pre>
27735 Value   Description
27736 ------  -------------------
27737  'n'     north
27738  's'     south
27739  'e'     east
27740  'w'     west
27741  'nw'    northwest
27742  'sw'    southwest
27743  'se'    southeast
27744  'ne'    northeast
27745  'hd'    horizontal drag
27746  'all'   all
27747 </pre>
27748  * <p>Here's an example showing the creation of a typical Resizable:</p>
27749  * <pre><code>
27750 var resizer = new Roo.Resizable("element-id", {
27751     handles: 'all',
27752     minWidth: 200,
27753     minHeight: 100,
27754     maxWidth: 500,
27755     maxHeight: 400,
27756     pinned: true
27757 });
27758 resizer.on("resize", myHandler);
27759 </code></pre>
27760  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27761  * resizer.east.setDisplayed(false);</p>
27762  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27763  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27764  * resize operation's new size (defaults to [0, 0])
27765  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27766  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27767  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27768  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27769  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27770  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27771  * @cfg {Number} width The width of the element in pixels (defaults to null)
27772  * @cfg {Number} height The height of the element in pixels (defaults to null)
27773  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27774  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27775  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27776  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27777  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27778  * in favor of the handles config option (defaults to false)
27779  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27780  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27781  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27782  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27783  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27784  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27785  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27786  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27787  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27788  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27789  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27790  * @constructor
27791  * Create a new resizable component
27792  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27793  * @param {Object} config configuration options
27794   */
27795 Roo.Resizable = function(el, config)
27796 {
27797     this.el = Roo.get(el);
27798
27799     if(config && config.wrap){
27800         config.resizeChild = this.el;
27801         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27802         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27803         this.el.setStyle("overflow", "hidden");
27804         this.el.setPositioning(config.resizeChild.getPositioning());
27805         config.resizeChild.clearPositioning();
27806         if(!config.width || !config.height){
27807             var csize = config.resizeChild.getSize();
27808             this.el.setSize(csize.width, csize.height);
27809         }
27810         if(config.pinned && !config.adjustments){
27811             config.adjustments = "auto";
27812         }
27813     }
27814
27815     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27816     this.proxy.unselectable();
27817     this.proxy.enableDisplayMode('block');
27818
27819     Roo.apply(this, config);
27820
27821     if(this.pinned){
27822         this.disableTrackOver = true;
27823         this.el.addClass("x-resizable-pinned");
27824     }
27825     // if the element isn't positioned, make it relative
27826     var position = this.el.getStyle("position");
27827     if(position != "absolute" && position != "fixed"){
27828         this.el.setStyle("position", "relative");
27829     }
27830     if(!this.handles){ // no handles passed, must be legacy style
27831         this.handles = 's,e,se';
27832         if(this.multiDirectional){
27833             this.handles += ',n,w';
27834         }
27835     }
27836     if(this.handles == "all"){
27837         this.handles = "n s e w ne nw se sw";
27838     }
27839     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27840     var ps = Roo.Resizable.positions;
27841     for(var i = 0, len = hs.length; i < len; i++){
27842         if(hs[i] && ps[hs[i]]){
27843             var pos = ps[hs[i]];
27844             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27845         }
27846     }
27847     // legacy
27848     this.corner = this.southeast;
27849     
27850     // updateBox = the box can move..
27851     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27852         this.updateBox = true;
27853     }
27854
27855     this.activeHandle = null;
27856
27857     if(this.resizeChild){
27858         if(typeof this.resizeChild == "boolean"){
27859             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27860         }else{
27861             this.resizeChild = Roo.get(this.resizeChild, true);
27862         }
27863     }
27864     
27865     if(this.adjustments == "auto"){
27866         var rc = this.resizeChild;
27867         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27868         if(rc && (hw || hn)){
27869             rc.position("relative");
27870             rc.setLeft(hw ? hw.el.getWidth() : 0);
27871             rc.setTop(hn ? hn.el.getHeight() : 0);
27872         }
27873         this.adjustments = [
27874             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27875             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27876         ];
27877     }
27878
27879     if(this.draggable){
27880         this.dd = this.dynamic ?
27881             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27882         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27883     }
27884
27885     // public events
27886     this.addEvents({
27887         /**
27888          * @event beforeresize
27889          * Fired before resize is allowed. Set enabled to false to cancel resize.
27890          * @param {Roo.Resizable} this
27891          * @param {Roo.EventObject} e The mousedown event
27892          */
27893         "beforeresize" : true,
27894         /**
27895          * @event resize
27896          * Fired after a resize.
27897          * @param {Roo.Resizable} this
27898          * @param {Number} width The new width
27899          * @param {Number} height The new height
27900          * @param {Roo.EventObject} e The mouseup event
27901          */
27902         "resize" : true
27903     });
27904
27905     if(this.width !== null && this.height !== null){
27906         this.resizeTo(this.width, this.height);
27907     }else{
27908         this.updateChildSize();
27909     }
27910     if(Roo.isIE){
27911         this.el.dom.style.zoom = 1;
27912     }
27913     Roo.Resizable.superclass.constructor.call(this);
27914 };
27915
27916 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27917         resizeChild : false,
27918         adjustments : [0, 0],
27919         minWidth : 5,
27920         minHeight : 5,
27921         maxWidth : 10000,
27922         maxHeight : 10000,
27923         enabled : true,
27924         animate : false,
27925         duration : .35,
27926         dynamic : false,
27927         handles : false,
27928         multiDirectional : false,
27929         disableTrackOver : false,
27930         easing : 'easeOutStrong',
27931         widthIncrement : 0,
27932         heightIncrement : 0,
27933         pinned : false,
27934         width : null,
27935         height : null,
27936         preserveRatio : false,
27937         transparent: false,
27938         minX: 0,
27939         minY: 0,
27940         draggable: false,
27941
27942         /**
27943          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27944          */
27945         constrainTo: undefined,
27946         /**
27947          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27948          */
27949         resizeRegion: undefined,
27950
27951
27952     /**
27953      * Perform a manual resize
27954      * @param {Number} width
27955      * @param {Number} height
27956      */
27957     resizeTo : function(width, height){
27958         this.el.setSize(width, height);
27959         this.updateChildSize();
27960         this.fireEvent("resize", this, width, height, null);
27961     },
27962
27963     // private
27964     startSizing : function(e, handle){
27965         this.fireEvent("beforeresize", this, e);
27966         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27967
27968             if(!this.overlay){
27969                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27970                 this.overlay.unselectable();
27971                 this.overlay.enableDisplayMode("block");
27972                 this.overlay.on("mousemove", this.onMouseMove, this);
27973                 this.overlay.on("mouseup", this.onMouseUp, this);
27974             }
27975             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27976
27977             this.resizing = true;
27978             this.startBox = this.el.getBox();
27979             this.startPoint = e.getXY();
27980             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27981                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27982
27983             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27984             this.overlay.show();
27985
27986             if(this.constrainTo) {
27987                 var ct = Roo.get(this.constrainTo);
27988                 this.resizeRegion = ct.getRegion().adjust(
27989                     ct.getFrameWidth('t'),
27990                     ct.getFrameWidth('l'),
27991                     -ct.getFrameWidth('b'),
27992                     -ct.getFrameWidth('r')
27993                 );
27994             }
27995
27996             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27997             this.proxy.show();
27998             this.proxy.setBox(this.startBox);
27999             if(!this.dynamic){
28000                 this.proxy.setStyle('visibility', 'visible');
28001             }
28002         }
28003     },
28004
28005     // private
28006     onMouseDown : function(handle, e){
28007         if(this.enabled){
28008             e.stopEvent();
28009             this.activeHandle = handle;
28010             this.startSizing(e, handle);
28011         }
28012     },
28013
28014     // private
28015     onMouseUp : function(e){
28016         var size = this.resizeElement();
28017         this.resizing = false;
28018         this.handleOut();
28019         this.overlay.hide();
28020         this.proxy.hide();
28021         this.fireEvent("resize", this, size.width, size.height, e);
28022     },
28023
28024     // private
28025     updateChildSize : function(){
28026         if(this.resizeChild){
28027             var el = this.el;
28028             var child = this.resizeChild;
28029             var adj = this.adjustments;
28030             if(el.dom.offsetWidth){
28031                 var b = el.getSize(true);
28032                 child.setSize(b.width+adj[0], b.height+adj[1]);
28033             }
28034             // Second call here for IE
28035             // The first call enables instant resizing and
28036             // the second call corrects scroll bars if they
28037             // exist
28038             if(Roo.isIE){
28039                 setTimeout(function(){
28040                     if(el.dom.offsetWidth){
28041                         var b = el.getSize(true);
28042                         child.setSize(b.width+adj[0], b.height+adj[1]);
28043                     }
28044                 }, 10);
28045             }
28046         }
28047     },
28048
28049     // private
28050     snap : function(value, inc, min){
28051         if(!inc || !value) return value;
28052         var newValue = value;
28053         var m = value % inc;
28054         if(m > 0){
28055             if(m > (inc/2)){
28056                 newValue = value + (inc-m);
28057             }else{
28058                 newValue = value - m;
28059             }
28060         }
28061         return Math.max(min, newValue);
28062     },
28063
28064     // private
28065     resizeElement : function(){
28066         var box = this.proxy.getBox();
28067         if(this.updateBox){
28068             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28069         }else{
28070             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28071         }
28072         this.updateChildSize();
28073         if(!this.dynamic){
28074             this.proxy.hide();
28075         }
28076         return box;
28077     },
28078
28079     // private
28080     constrain : function(v, diff, m, mx){
28081         if(v - diff < m){
28082             diff = v - m;
28083         }else if(v - diff > mx){
28084             diff = mx - v;
28085         }
28086         return diff;
28087     },
28088
28089     // private
28090     onMouseMove : function(e){
28091         if(this.enabled){
28092             try{// try catch so if something goes wrong the user doesn't get hung
28093
28094             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28095                 return;
28096             }
28097
28098             //var curXY = this.startPoint;
28099             var curSize = this.curSize || this.startBox;
28100             var x = this.startBox.x, y = this.startBox.y;
28101             var ox = x, oy = y;
28102             var w = curSize.width, h = curSize.height;
28103             var ow = w, oh = h;
28104             var mw = this.minWidth, mh = this.minHeight;
28105             var mxw = this.maxWidth, mxh = this.maxHeight;
28106             var wi = this.widthIncrement;
28107             var hi = this.heightIncrement;
28108
28109             var eventXY = e.getXY();
28110             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28111             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28112
28113             var pos = this.activeHandle.position;
28114
28115             switch(pos){
28116                 case "east":
28117                     w += diffX;
28118                     w = Math.min(Math.max(mw, w), mxw);
28119                     break;
28120              
28121                 case "south":
28122                     h += diffY;
28123                     h = Math.min(Math.max(mh, h), mxh);
28124                     break;
28125                 case "southeast":
28126                     w += diffX;
28127                     h += diffY;
28128                     w = Math.min(Math.max(mw, w), mxw);
28129                     h = Math.min(Math.max(mh, h), mxh);
28130                     break;
28131                 case "north":
28132                     diffY = this.constrain(h, diffY, mh, mxh);
28133                     y += diffY;
28134                     h -= diffY;
28135                     break;
28136                 case "hdrag":
28137                     
28138                     if (wi) {
28139                         var adiffX = Math.abs(diffX);
28140                         var sub = (adiffX % wi); // how much 
28141                         if (sub > (wi/2)) { // far enough to snap
28142                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28143                         } else {
28144                             // remove difference.. 
28145                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28146                         }
28147                     }
28148                     x += diffX;
28149                     x = Math.max(this.minX, x);
28150                     break;
28151                 case "west":
28152                     diffX = this.constrain(w, diffX, mw, mxw);
28153                     x += diffX;
28154                     w -= diffX;
28155                     break;
28156                 case "northeast":
28157                     w += diffX;
28158                     w = Math.min(Math.max(mw, w), mxw);
28159                     diffY = this.constrain(h, diffY, mh, mxh);
28160                     y += diffY;
28161                     h -= diffY;
28162                     break;
28163                 case "northwest":
28164                     diffX = this.constrain(w, diffX, mw, mxw);
28165                     diffY = this.constrain(h, diffY, mh, mxh);
28166                     y += diffY;
28167                     h -= diffY;
28168                     x += diffX;
28169                     w -= diffX;
28170                     break;
28171                case "southwest":
28172                     diffX = this.constrain(w, diffX, mw, mxw);
28173                     h += diffY;
28174                     h = Math.min(Math.max(mh, h), mxh);
28175                     x += diffX;
28176                     w -= diffX;
28177                     break;
28178             }
28179
28180             var sw = this.snap(w, wi, mw);
28181             var sh = this.snap(h, hi, mh);
28182             if(sw != w || sh != h){
28183                 switch(pos){
28184                     case "northeast":
28185                         y -= sh - h;
28186                     break;
28187                     case "north":
28188                         y -= sh - h;
28189                         break;
28190                     case "southwest":
28191                         x -= sw - w;
28192                     break;
28193                     case "west":
28194                         x -= sw - w;
28195                         break;
28196                     case "northwest":
28197                         x -= sw - w;
28198                         y -= sh - h;
28199                     break;
28200                 }
28201                 w = sw;
28202                 h = sh;
28203             }
28204
28205             if(this.preserveRatio){
28206                 switch(pos){
28207                     case "southeast":
28208                     case "east":
28209                         h = oh * (w/ow);
28210                         h = Math.min(Math.max(mh, h), mxh);
28211                         w = ow * (h/oh);
28212                        break;
28213                     case "south":
28214                         w = ow * (h/oh);
28215                         w = Math.min(Math.max(mw, w), mxw);
28216                         h = oh * (w/ow);
28217                         break;
28218                     case "northeast":
28219                         w = ow * (h/oh);
28220                         w = Math.min(Math.max(mw, w), mxw);
28221                         h = oh * (w/ow);
28222                     break;
28223                     case "north":
28224                         var tw = w;
28225                         w = ow * (h/oh);
28226                         w = Math.min(Math.max(mw, w), mxw);
28227                         h = oh * (w/ow);
28228                         x += (tw - w) / 2;
28229                         break;
28230                     case "southwest":
28231                         h = oh * (w/ow);
28232                         h = Math.min(Math.max(mh, h), mxh);
28233                         var tw = w;
28234                         w = ow * (h/oh);
28235                         x += tw - w;
28236                         break;
28237                     case "west":
28238                         var th = h;
28239                         h = oh * (w/ow);
28240                         h = Math.min(Math.max(mh, h), mxh);
28241                         y += (th - h) / 2;
28242                         var tw = w;
28243                         w = ow * (h/oh);
28244                         x += tw - w;
28245                        break;
28246                     case "northwest":
28247                         var tw = w;
28248                         var th = h;
28249                         h = oh * (w/ow);
28250                         h = Math.min(Math.max(mh, h), mxh);
28251                         w = ow * (h/oh);
28252                         y += th - h;
28253                         x += tw - w;
28254                        break;
28255
28256                 }
28257             }
28258             if (pos == 'hdrag') {
28259                 w = ow;
28260             }
28261             this.proxy.setBounds(x, y, w, h);
28262             if(this.dynamic){
28263                 this.resizeElement();
28264             }
28265             }catch(e){}
28266         }
28267     },
28268
28269     // private
28270     handleOver : function(){
28271         if(this.enabled){
28272             this.el.addClass("x-resizable-over");
28273         }
28274     },
28275
28276     // private
28277     handleOut : function(){
28278         if(!this.resizing){
28279             this.el.removeClass("x-resizable-over");
28280         }
28281     },
28282
28283     /**
28284      * Returns the element this component is bound to.
28285      * @return {Roo.Element}
28286      */
28287     getEl : function(){
28288         return this.el;
28289     },
28290
28291     /**
28292      * Returns the resizeChild element (or null).
28293      * @return {Roo.Element}
28294      */
28295     getResizeChild : function(){
28296         return this.resizeChild;
28297     },
28298
28299     /**
28300      * Destroys this resizable. If the element was wrapped and
28301      * removeEl is not true then the element remains.
28302      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28303      */
28304     destroy : function(removeEl){
28305         this.proxy.remove();
28306         if(this.overlay){
28307             this.overlay.removeAllListeners();
28308             this.overlay.remove();
28309         }
28310         var ps = Roo.Resizable.positions;
28311         for(var k in ps){
28312             if(typeof ps[k] != "function" && this[ps[k]]){
28313                 var h = this[ps[k]];
28314                 h.el.removeAllListeners();
28315                 h.el.remove();
28316             }
28317         }
28318         if(removeEl){
28319             this.el.update("");
28320             this.el.remove();
28321         }
28322     }
28323 });
28324
28325 // private
28326 // hash to map config positions to true positions
28327 Roo.Resizable.positions = {
28328     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28329     hd: "hdrag"
28330 };
28331
28332 // private
28333 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28334     if(!this.tpl){
28335         // only initialize the template if resizable is used
28336         var tpl = Roo.DomHelper.createTemplate(
28337             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28338         );
28339         tpl.compile();
28340         Roo.Resizable.Handle.prototype.tpl = tpl;
28341     }
28342     this.position = pos;
28343     this.rz = rz;
28344     // show north drag fro topdra
28345     var handlepos = pos == 'hdrag' ? 'north' : pos;
28346     
28347     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28348     if (pos == 'hdrag') {
28349         this.el.setStyle('cursor', 'pointer');
28350     }
28351     this.el.unselectable();
28352     if(transparent){
28353         this.el.setOpacity(0);
28354     }
28355     this.el.on("mousedown", this.onMouseDown, this);
28356     if(!disableTrackOver){
28357         this.el.on("mouseover", this.onMouseOver, this);
28358         this.el.on("mouseout", this.onMouseOut, this);
28359     }
28360 };
28361
28362 // private
28363 Roo.Resizable.Handle.prototype = {
28364     afterResize : function(rz){
28365         // do nothing
28366     },
28367     // private
28368     onMouseDown : function(e){
28369         this.rz.onMouseDown(this, e);
28370     },
28371     // private
28372     onMouseOver : function(e){
28373         this.rz.handleOver(this, e);
28374     },
28375     // private
28376     onMouseOut : function(e){
28377         this.rz.handleOut(this, e);
28378     }
28379 };/*
28380  * Based on:
28381  * Ext JS Library 1.1.1
28382  * Copyright(c) 2006-2007, Ext JS, LLC.
28383  *
28384  * Originally Released Under LGPL - original licence link has changed is not relivant.
28385  *
28386  * Fork - LGPL
28387  * <script type="text/javascript">
28388  */
28389
28390 /**
28391  * @class Roo.Editor
28392  * @extends Roo.Component
28393  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28394  * @constructor
28395  * Create a new Editor
28396  * @param {Roo.form.Field} field The Field object (or descendant)
28397  * @param {Object} config The config object
28398  */
28399 Roo.Editor = function(field, config){
28400     Roo.Editor.superclass.constructor.call(this, config);
28401     this.field = field;
28402     this.addEvents({
28403         /**
28404              * @event beforestartedit
28405              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28406              * false from the handler of this event.
28407              * @param {Editor} this
28408              * @param {Roo.Element} boundEl The underlying element bound to this editor
28409              * @param {Mixed} value The field value being set
28410              */
28411         "beforestartedit" : true,
28412         /**
28413              * @event startedit
28414              * Fires when this editor is displayed
28415              * @param {Roo.Element} boundEl The underlying element bound to this editor
28416              * @param {Mixed} value The starting field value
28417              */
28418         "startedit" : true,
28419         /**
28420              * @event beforecomplete
28421              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28422              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28423              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28424              * event will not fire since no edit actually occurred.
28425              * @param {Editor} this
28426              * @param {Mixed} value The current field value
28427              * @param {Mixed} startValue The original field value
28428              */
28429         "beforecomplete" : true,
28430         /**
28431              * @event complete
28432              * Fires after editing is complete and any changed value has been written to the underlying field.
28433              * @param {Editor} this
28434              * @param {Mixed} value The current field value
28435              * @param {Mixed} startValue The original field value
28436              */
28437         "complete" : true,
28438         /**
28439          * @event specialkey
28440          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28441          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28442          * @param {Roo.form.Field} this
28443          * @param {Roo.EventObject} e The event object
28444          */
28445         "specialkey" : true
28446     });
28447 };
28448
28449 Roo.extend(Roo.Editor, Roo.Component, {
28450     /**
28451      * @cfg {Boolean/String} autosize
28452      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28453      * or "height" to adopt the height only (defaults to false)
28454      */
28455     /**
28456      * @cfg {Boolean} revertInvalid
28457      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28458      * validation fails (defaults to true)
28459      */
28460     /**
28461      * @cfg {Boolean} ignoreNoChange
28462      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28463      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28464      * will never be ignored.
28465      */
28466     /**
28467      * @cfg {Boolean} hideEl
28468      * False to keep the bound element visible while the editor is displayed (defaults to true)
28469      */
28470     /**
28471      * @cfg {Mixed} value
28472      * The data value of the underlying field (defaults to "")
28473      */
28474     value : "",
28475     /**
28476      * @cfg {String} alignment
28477      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28478      */
28479     alignment: "c-c?",
28480     /**
28481      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28482      * for bottom-right shadow (defaults to "frame")
28483      */
28484     shadow : "frame",
28485     /**
28486      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28487      */
28488     constrain : false,
28489     /**
28490      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28491      */
28492     completeOnEnter : false,
28493     /**
28494      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28495      */
28496     cancelOnEsc : false,
28497     /**
28498      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28499      */
28500     updateEl : false,
28501
28502     // private
28503     onRender : function(ct, position){
28504         this.el = new Roo.Layer({
28505             shadow: this.shadow,
28506             cls: "x-editor",
28507             parentEl : ct,
28508             shim : this.shim,
28509             shadowOffset:4,
28510             id: this.id,
28511             constrain: this.constrain
28512         });
28513         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28514         if(this.field.msgTarget != 'title'){
28515             this.field.msgTarget = 'qtip';
28516         }
28517         this.field.render(this.el);
28518         if(Roo.isGecko){
28519             this.field.el.dom.setAttribute('autocomplete', 'off');
28520         }
28521         this.field.on("specialkey", this.onSpecialKey, this);
28522         if(this.swallowKeys){
28523             this.field.el.swallowEvent(['keydown','keypress']);
28524         }
28525         this.field.show();
28526         this.field.on("blur", this.onBlur, this);
28527         if(this.field.grow){
28528             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28529         }
28530     },
28531
28532     onSpecialKey : function(field, e)
28533     {
28534         //Roo.log('editor onSpecialKey');
28535         if(this.completeOnEnter && e.getKey() == e.ENTER){
28536             e.stopEvent();
28537             this.completeEdit();
28538             return;
28539         }
28540         // do not fire special key otherwise it might hide close the editor...
28541         if(e.getKey() == e.ENTER){    
28542             return;
28543         }
28544         if(this.cancelOnEsc && e.getKey() == e.ESC){
28545             this.cancelEdit();
28546             return;
28547         } 
28548         this.fireEvent('specialkey', field, e);
28549     
28550     },
28551
28552     /**
28553      * Starts the editing process and shows the editor.
28554      * @param {String/HTMLElement/Element} el The element to edit
28555      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28556       * to the innerHTML of el.
28557      */
28558     startEdit : function(el, value){
28559         if(this.editing){
28560             this.completeEdit();
28561         }
28562         this.boundEl = Roo.get(el);
28563         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28564         if(!this.rendered){
28565             this.render(this.parentEl || document.body);
28566         }
28567         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28568             return;
28569         }
28570         this.startValue = v;
28571         this.field.setValue(v);
28572         if(this.autoSize){
28573             var sz = this.boundEl.getSize();
28574             switch(this.autoSize){
28575                 case "width":
28576                 this.setSize(sz.width,  "");
28577                 break;
28578                 case "height":
28579                 this.setSize("",  sz.height);
28580                 break;
28581                 default:
28582                 this.setSize(sz.width,  sz.height);
28583             }
28584         }
28585         this.el.alignTo(this.boundEl, this.alignment);
28586         this.editing = true;
28587         if(Roo.QuickTips){
28588             Roo.QuickTips.disable();
28589         }
28590         this.show();
28591     },
28592
28593     /**
28594      * Sets the height and width of this editor.
28595      * @param {Number} width The new width
28596      * @param {Number} height The new height
28597      */
28598     setSize : function(w, h){
28599         this.field.setSize(w, h);
28600         if(this.el){
28601             this.el.sync();
28602         }
28603     },
28604
28605     /**
28606      * Realigns the editor to the bound field based on the current alignment config value.
28607      */
28608     realign : function(){
28609         this.el.alignTo(this.boundEl, this.alignment);
28610     },
28611
28612     /**
28613      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28614      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28615      */
28616     completeEdit : function(remainVisible){
28617         if(!this.editing){
28618             return;
28619         }
28620         var v = this.getValue();
28621         if(this.revertInvalid !== false && !this.field.isValid()){
28622             v = this.startValue;
28623             this.cancelEdit(true);
28624         }
28625         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28626             this.editing = false;
28627             this.hide();
28628             return;
28629         }
28630         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28631             this.editing = false;
28632             if(this.updateEl && this.boundEl){
28633                 this.boundEl.update(v);
28634             }
28635             if(remainVisible !== true){
28636                 this.hide();
28637             }
28638             this.fireEvent("complete", this, v, this.startValue);
28639         }
28640     },
28641
28642     // private
28643     onShow : function(){
28644         this.el.show();
28645         if(this.hideEl !== false){
28646             this.boundEl.hide();
28647         }
28648         this.field.show();
28649         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28650             this.fixIEFocus = true;
28651             this.deferredFocus.defer(50, this);
28652         }else{
28653             this.field.focus();
28654         }
28655         this.fireEvent("startedit", this.boundEl, this.startValue);
28656     },
28657
28658     deferredFocus : function(){
28659         if(this.editing){
28660             this.field.focus();
28661         }
28662     },
28663
28664     /**
28665      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28666      * reverted to the original starting value.
28667      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28668      * cancel (defaults to false)
28669      */
28670     cancelEdit : function(remainVisible){
28671         if(this.editing){
28672             this.setValue(this.startValue);
28673             if(remainVisible !== true){
28674                 this.hide();
28675             }
28676         }
28677     },
28678
28679     // private
28680     onBlur : function(){
28681         if(this.allowBlur !== true && this.editing){
28682             this.completeEdit();
28683         }
28684     },
28685
28686     // private
28687     onHide : function(){
28688         if(this.editing){
28689             this.completeEdit();
28690             return;
28691         }
28692         this.field.blur();
28693         if(this.field.collapse){
28694             this.field.collapse();
28695         }
28696         this.el.hide();
28697         if(this.hideEl !== false){
28698             this.boundEl.show();
28699         }
28700         if(Roo.QuickTips){
28701             Roo.QuickTips.enable();
28702         }
28703     },
28704
28705     /**
28706      * Sets the data value of the editor
28707      * @param {Mixed} value Any valid value supported by the underlying field
28708      */
28709     setValue : function(v){
28710         this.field.setValue(v);
28711     },
28712
28713     /**
28714      * Gets the data value of the editor
28715      * @return {Mixed} The data value
28716      */
28717     getValue : function(){
28718         return this.field.getValue();
28719     }
28720 });/*
28721  * Based on:
28722  * Ext JS Library 1.1.1
28723  * Copyright(c) 2006-2007, Ext JS, LLC.
28724  *
28725  * Originally Released Under LGPL - original licence link has changed is not relivant.
28726  *
28727  * Fork - LGPL
28728  * <script type="text/javascript">
28729  */
28730  
28731 /**
28732  * @class Roo.BasicDialog
28733  * @extends Roo.util.Observable
28734  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28735  * <pre><code>
28736 var dlg = new Roo.BasicDialog("my-dlg", {
28737     height: 200,
28738     width: 300,
28739     minHeight: 100,
28740     minWidth: 150,
28741     modal: true,
28742     proxyDrag: true,
28743     shadow: true
28744 });
28745 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28746 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28747 dlg.addButton('Cancel', dlg.hide, dlg);
28748 dlg.show();
28749 </code></pre>
28750   <b>A Dialog should always be a direct child of the body element.</b>
28751  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28752  * @cfg {String} title Default text to display in the title bar (defaults to null)
28753  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28754  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28755  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28756  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28757  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28758  * (defaults to null with no animation)
28759  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28760  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28761  * property for valid values (defaults to 'all')
28762  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28763  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28764  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28765  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28766  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28767  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28768  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28769  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28770  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28771  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28772  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28773  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28774  * draggable = true (defaults to false)
28775  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28776  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28777  * shadow (defaults to false)
28778  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28779  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28780  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28781  * @cfg {Array} buttons Array of buttons
28782  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28783  * @constructor
28784  * Create a new BasicDialog.
28785  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28786  * @param {Object} config Configuration options
28787  */
28788 Roo.BasicDialog = function(el, config){
28789     this.el = Roo.get(el);
28790     var dh = Roo.DomHelper;
28791     if(!this.el && config && config.autoCreate){
28792         if(typeof config.autoCreate == "object"){
28793             if(!config.autoCreate.id){
28794                 config.autoCreate.id = el;
28795             }
28796             this.el = dh.append(document.body,
28797                         config.autoCreate, true);
28798         }else{
28799             this.el = dh.append(document.body,
28800                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28801         }
28802     }
28803     el = this.el;
28804     el.setDisplayed(true);
28805     el.hide = this.hideAction;
28806     this.id = el.id;
28807     el.addClass("x-dlg");
28808
28809     Roo.apply(this, config);
28810
28811     this.proxy = el.createProxy("x-dlg-proxy");
28812     this.proxy.hide = this.hideAction;
28813     this.proxy.setOpacity(.5);
28814     this.proxy.hide();
28815
28816     if(config.width){
28817         el.setWidth(config.width);
28818     }
28819     if(config.height){
28820         el.setHeight(config.height);
28821     }
28822     this.size = el.getSize();
28823     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28824         this.xy = [config.x,config.y];
28825     }else{
28826         this.xy = el.getCenterXY(true);
28827     }
28828     /** The header element @type Roo.Element */
28829     this.header = el.child("> .x-dlg-hd");
28830     /** The body element @type Roo.Element */
28831     this.body = el.child("> .x-dlg-bd");
28832     /** The footer element @type Roo.Element */
28833     this.footer = el.child("> .x-dlg-ft");
28834
28835     if(!this.header){
28836         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28837     }
28838     if(!this.body){
28839         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28840     }
28841
28842     this.header.unselectable();
28843     if(this.title){
28844         this.header.update(this.title);
28845     }
28846     // this element allows the dialog to be focused for keyboard event
28847     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28848     this.focusEl.swallowEvent("click", true);
28849
28850     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28851
28852     // wrap the body and footer for special rendering
28853     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28854     if(this.footer){
28855         this.bwrap.dom.appendChild(this.footer.dom);
28856     }
28857
28858     this.bg = this.el.createChild({
28859         tag: "div", cls:"x-dlg-bg",
28860         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28861     });
28862     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28863
28864
28865     if(this.autoScroll !== false && !this.autoTabs){
28866         this.body.setStyle("overflow", "auto");
28867     }
28868
28869     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28870
28871     if(this.closable !== false){
28872         this.el.addClass("x-dlg-closable");
28873         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28874         this.close.on("click", this.closeClick, this);
28875         this.close.addClassOnOver("x-dlg-close-over");
28876     }
28877     if(this.collapsible !== false){
28878         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28879         this.collapseBtn.on("click", this.collapseClick, this);
28880         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28881         this.header.on("dblclick", this.collapseClick, this);
28882     }
28883     if(this.resizable !== false){
28884         this.el.addClass("x-dlg-resizable");
28885         this.resizer = new Roo.Resizable(el, {
28886             minWidth: this.minWidth || 80,
28887             minHeight:this.minHeight || 80,
28888             handles: this.resizeHandles || "all",
28889             pinned: true
28890         });
28891         this.resizer.on("beforeresize", this.beforeResize, this);
28892         this.resizer.on("resize", this.onResize, this);
28893     }
28894     if(this.draggable !== false){
28895         el.addClass("x-dlg-draggable");
28896         if (!this.proxyDrag) {
28897             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28898         }
28899         else {
28900             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28901         }
28902         dd.setHandleElId(this.header.id);
28903         dd.endDrag = this.endMove.createDelegate(this);
28904         dd.startDrag = this.startMove.createDelegate(this);
28905         dd.onDrag = this.onDrag.createDelegate(this);
28906         dd.scroll = false;
28907         this.dd = dd;
28908     }
28909     if(this.modal){
28910         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28911         this.mask.enableDisplayMode("block");
28912         this.mask.hide();
28913         this.el.addClass("x-dlg-modal");
28914     }
28915     if(this.shadow){
28916         this.shadow = new Roo.Shadow({
28917             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28918             offset : this.shadowOffset
28919         });
28920     }else{
28921         this.shadowOffset = 0;
28922     }
28923     if(Roo.useShims && this.shim !== false){
28924         this.shim = this.el.createShim();
28925         this.shim.hide = this.hideAction;
28926         this.shim.hide();
28927     }else{
28928         this.shim = false;
28929     }
28930     if(this.autoTabs){
28931         this.initTabs();
28932     }
28933     if (this.buttons) { 
28934         var bts= this.buttons;
28935         this.buttons = [];
28936         Roo.each(bts, function(b) {
28937             this.addButton(b);
28938         }, this);
28939     }
28940     
28941     
28942     this.addEvents({
28943         /**
28944          * @event keydown
28945          * Fires when a key is pressed
28946          * @param {Roo.BasicDialog} this
28947          * @param {Roo.EventObject} e
28948          */
28949         "keydown" : true,
28950         /**
28951          * @event move
28952          * Fires when this dialog is moved by the user.
28953          * @param {Roo.BasicDialog} this
28954          * @param {Number} x The new page X
28955          * @param {Number} y The new page Y
28956          */
28957         "move" : true,
28958         /**
28959          * @event resize
28960          * Fires when this dialog is resized by the user.
28961          * @param {Roo.BasicDialog} this
28962          * @param {Number} width The new width
28963          * @param {Number} height The new height
28964          */
28965         "resize" : true,
28966         /**
28967          * @event beforehide
28968          * Fires before this dialog is hidden.
28969          * @param {Roo.BasicDialog} this
28970          */
28971         "beforehide" : true,
28972         /**
28973          * @event hide
28974          * Fires when this dialog is hidden.
28975          * @param {Roo.BasicDialog} this
28976          */
28977         "hide" : true,
28978         /**
28979          * @event beforeshow
28980          * Fires before this dialog is shown.
28981          * @param {Roo.BasicDialog} this
28982          */
28983         "beforeshow" : true,
28984         /**
28985          * @event show
28986          * Fires when this dialog is shown.
28987          * @param {Roo.BasicDialog} this
28988          */
28989         "show" : true
28990     });
28991     el.on("keydown", this.onKeyDown, this);
28992     el.on("mousedown", this.toFront, this);
28993     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28994     this.el.hide();
28995     Roo.DialogManager.register(this);
28996     Roo.BasicDialog.superclass.constructor.call(this);
28997 };
28998
28999 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
29000     shadowOffset: Roo.isIE ? 6 : 5,
29001     minHeight: 80,
29002     minWidth: 200,
29003     minButtonWidth: 75,
29004     defaultButton: null,
29005     buttonAlign: "right",
29006     tabTag: 'div',
29007     firstShow: true,
29008
29009     /**
29010      * Sets the dialog title text
29011      * @param {String} text The title text to display
29012      * @return {Roo.BasicDialog} this
29013      */
29014     setTitle : function(text){
29015         this.header.update(text);
29016         return this;
29017     },
29018
29019     // private
29020     closeClick : function(){
29021         this.hide();
29022     },
29023
29024     // private
29025     collapseClick : function(){
29026         this[this.collapsed ? "expand" : "collapse"]();
29027     },
29028
29029     /**
29030      * Collapses the dialog to its minimized state (only the title bar is visible).
29031      * Equivalent to the user clicking the collapse dialog button.
29032      */
29033     collapse : function(){
29034         if(!this.collapsed){
29035             this.collapsed = true;
29036             this.el.addClass("x-dlg-collapsed");
29037             this.restoreHeight = this.el.getHeight();
29038             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29039         }
29040     },
29041
29042     /**
29043      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29044      * clicking the expand dialog button.
29045      */
29046     expand : function(){
29047         if(this.collapsed){
29048             this.collapsed = false;
29049             this.el.removeClass("x-dlg-collapsed");
29050             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29051         }
29052     },
29053
29054     /**
29055      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29056      * @return {Roo.TabPanel} The tabs component
29057      */
29058     initTabs : function(){
29059         var tabs = this.getTabs();
29060         while(tabs.getTab(0)){
29061             tabs.removeTab(0);
29062         }
29063         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29064             var dom = el.dom;
29065             tabs.addTab(Roo.id(dom), dom.title);
29066             dom.title = "";
29067         });
29068         tabs.activate(0);
29069         return tabs;
29070     },
29071
29072     // private
29073     beforeResize : function(){
29074         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29075     },
29076
29077     // private
29078     onResize : function(){
29079         this.refreshSize();
29080         this.syncBodyHeight();
29081         this.adjustAssets();
29082         this.focus();
29083         this.fireEvent("resize", this, this.size.width, this.size.height);
29084     },
29085
29086     // private
29087     onKeyDown : function(e){
29088         if(this.isVisible()){
29089             this.fireEvent("keydown", this, e);
29090         }
29091     },
29092
29093     /**
29094      * Resizes the dialog.
29095      * @param {Number} width
29096      * @param {Number} height
29097      * @return {Roo.BasicDialog} this
29098      */
29099     resizeTo : function(width, height){
29100         this.el.setSize(width, height);
29101         this.size = {width: width, height: height};
29102         this.syncBodyHeight();
29103         if(this.fixedcenter){
29104             this.center();
29105         }
29106         if(this.isVisible()){
29107             this.constrainXY();
29108             this.adjustAssets();
29109         }
29110         this.fireEvent("resize", this, width, height);
29111         return this;
29112     },
29113
29114
29115     /**
29116      * Resizes the dialog to fit the specified content size.
29117      * @param {Number} width
29118      * @param {Number} height
29119      * @return {Roo.BasicDialog} this
29120      */
29121     setContentSize : function(w, h){
29122         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29123         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29124         //if(!this.el.isBorderBox()){
29125             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29126             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29127         //}
29128         if(this.tabs){
29129             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29130             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29131         }
29132         this.resizeTo(w, h);
29133         return this;
29134     },
29135
29136     /**
29137      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29138      * executed in response to a particular key being pressed while the dialog is active.
29139      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29140      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29141      * @param {Function} fn The function to call
29142      * @param {Object} scope (optional) The scope of the function
29143      * @return {Roo.BasicDialog} this
29144      */
29145     addKeyListener : function(key, fn, scope){
29146         var keyCode, shift, ctrl, alt;
29147         if(typeof key == "object" && !(key instanceof Array)){
29148             keyCode = key["key"];
29149             shift = key["shift"];
29150             ctrl = key["ctrl"];
29151             alt = key["alt"];
29152         }else{
29153             keyCode = key;
29154         }
29155         var handler = function(dlg, e){
29156             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29157                 var k = e.getKey();
29158                 if(keyCode instanceof Array){
29159                     for(var i = 0, len = keyCode.length; i < len; i++){
29160                         if(keyCode[i] == k){
29161                           fn.call(scope || window, dlg, k, e);
29162                           return;
29163                         }
29164                     }
29165                 }else{
29166                     if(k == keyCode){
29167                         fn.call(scope || window, dlg, k, e);
29168                     }
29169                 }
29170             }
29171         };
29172         this.on("keydown", handler);
29173         return this;
29174     },
29175
29176     /**
29177      * Returns the TabPanel component (creates it if it doesn't exist).
29178      * Note: If you wish to simply check for the existence of tabs without creating them,
29179      * check for a null 'tabs' property.
29180      * @return {Roo.TabPanel} The tabs component
29181      */
29182     getTabs : function(){
29183         if(!this.tabs){
29184             this.el.addClass("x-dlg-auto-tabs");
29185             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29186             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29187         }
29188         return this.tabs;
29189     },
29190
29191     /**
29192      * Adds a button to the footer section of the dialog.
29193      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29194      * object or a valid Roo.DomHelper element config
29195      * @param {Function} handler The function called when the button is clicked
29196      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29197      * @return {Roo.Button} The new button
29198      */
29199     addButton : function(config, handler, scope){
29200         var dh = Roo.DomHelper;
29201         if(!this.footer){
29202             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29203         }
29204         if(!this.btnContainer){
29205             var tb = this.footer.createChild({
29206
29207                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29208                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29209             }, null, true);
29210             this.btnContainer = tb.firstChild.firstChild.firstChild;
29211         }
29212         var bconfig = {
29213             handler: handler,
29214             scope: scope,
29215             minWidth: this.minButtonWidth,
29216             hideParent:true
29217         };
29218         if(typeof config == "string"){
29219             bconfig.text = config;
29220         }else{
29221             if(config.tag){
29222                 bconfig.dhconfig = config;
29223             }else{
29224                 Roo.apply(bconfig, config);
29225             }
29226         }
29227         var fc = false;
29228         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29229             bconfig.position = Math.max(0, bconfig.position);
29230             fc = this.btnContainer.childNodes[bconfig.position];
29231         }
29232          
29233         var btn = new Roo.Button(
29234             fc ? 
29235                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29236                 : this.btnContainer.appendChild(document.createElement("td")),
29237             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29238             bconfig
29239         );
29240         this.syncBodyHeight();
29241         if(!this.buttons){
29242             /**
29243              * Array of all the buttons that have been added to this dialog via addButton
29244              * @type Array
29245              */
29246             this.buttons = [];
29247         }
29248         this.buttons.push(btn);
29249         return btn;
29250     },
29251
29252     /**
29253      * Sets the default button to be focused when the dialog is displayed.
29254      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29255      * @return {Roo.BasicDialog} this
29256      */
29257     setDefaultButton : function(btn){
29258         this.defaultButton = btn;
29259         return this;
29260     },
29261
29262     // private
29263     getHeaderFooterHeight : function(safe){
29264         var height = 0;
29265         if(this.header){
29266            height += this.header.getHeight();
29267         }
29268         if(this.footer){
29269            var fm = this.footer.getMargins();
29270             height += (this.footer.getHeight()+fm.top+fm.bottom);
29271         }
29272         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29273         height += this.centerBg.getPadding("tb");
29274         return height;
29275     },
29276
29277     // private
29278     syncBodyHeight : function(){
29279         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29280         var height = this.size.height - this.getHeaderFooterHeight(false);
29281         bd.setHeight(height-bd.getMargins("tb"));
29282         var hh = this.header.getHeight();
29283         var h = this.size.height-hh;
29284         cb.setHeight(h);
29285         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29286         bw.setHeight(h-cb.getPadding("tb"));
29287         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29288         bd.setWidth(bw.getWidth(true));
29289         if(this.tabs){
29290             this.tabs.syncHeight();
29291             if(Roo.isIE){
29292                 this.tabs.el.repaint();
29293             }
29294         }
29295     },
29296
29297     /**
29298      * Restores the previous state of the dialog if Roo.state is configured.
29299      * @return {Roo.BasicDialog} this
29300      */
29301     restoreState : function(){
29302         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29303         if(box && box.width){
29304             this.xy = [box.x, box.y];
29305             this.resizeTo(box.width, box.height);
29306         }
29307         return this;
29308     },
29309
29310     // private
29311     beforeShow : function(){
29312         this.expand();
29313         if(this.fixedcenter){
29314             this.xy = this.el.getCenterXY(true);
29315         }
29316         if(this.modal){
29317             Roo.get(document.body).addClass("x-body-masked");
29318             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29319             this.mask.show();
29320         }
29321         this.constrainXY();
29322     },
29323
29324     // private
29325     animShow : function(){
29326         var b = Roo.get(this.animateTarget).getBox();
29327         this.proxy.setSize(b.width, b.height);
29328         this.proxy.setLocation(b.x, b.y);
29329         this.proxy.show();
29330         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29331                     true, .35, this.showEl.createDelegate(this));
29332     },
29333
29334     /**
29335      * Shows the dialog.
29336      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29337      * @return {Roo.BasicDialog} this
29338      */
29339     show : function(animateTarget){
29340         if (this.fireEvent("beforeshow", this) === false){
29341             return;
29342         }
29343         if(this.syncHeightBeforeShow){
29344             this.syncBodyHeight();
29345         }else if(this.firstShow){
29346             this.firstShow = false;
29347             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29348         }
29349         this.animateTarget = animateTarget || this.animateTarget;
29350         if(!this.el.isVisible()){
29351             this.beforeShow();
29352             if(this.animateTarget && Roo.get(this.animateTarget)){
29353                 this.animShow();
29354             }else{
29355                 this.showEl();
29356             }
29357         }
29358         return this;
29359     },
29360
29361     // private
29362     showEl : function(){
29363         this.proxy.hide();
29364         this.el.setXY(this.xy);
29365         this.el.show();
29366         this.adjustAssets(true);
29367         this.toFront();
29368         this.focus();
29369         // IE peekaboo bug - fix found by Dave Fenwick
29370         if(Roo.isIE){
29371             this.el.repaint();
29372         }
29373         this.fireEvent("show", this);
29374     },
29375
29376     /**
29377      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29378      * dialog itself will receive focus.
29379      */
29380     focus : function(){
29381         if(this.defaultButton){
29382             this.defaultButton.focus();
29383         }else{
29384             this.focusEl.focus();
29385         }
29386     },
29387
29388     // private
29389     constrainXY : function(){
29390         if(this.constraintoviewport !== false){
29391             if(!this.viewSize){
29392                 if(this.container){
29393                     var s = this.container.getSize();
29394                     this.viewSize = [s.width, s.height];
29395                 }else{
29396                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29397                 }
29398             }
29399             var s = Roo.get(this.container||document).getScroll();
29400
29401             var x = this.xy[0], y = this.xy[1];
29402             var w = this.size.width, h = this.size.height;
29403             var vw = this.viewSize[0], vh = this.viewSize[1];
29404             // only move it if it needs it
29405             var moved = false;
29406             // first validate right/bottom
29407             if(x + w > vw+s.left){
29408                 x = vw - w;
29409                 moved = true;
29410             }
29411             if(y + h > vh+s.top){
29412                 y = vh - h;
29413                 moved = true;
29414             }
29415             // then make sure top/left isn't negative
29416             if(x < s.left){
29417                 x = s.left;
29418                 moved = true;
29419             }
29420             if(y < s.top){
29421                 y = s.top;
29422                 moved = true;
29423             }
29424             if(moved){
29425                 // cache xy
29426                 this.xy = [x, y];
29427                 if(this.isVisible()){
29428                     this.el.setLocation(x, y);
29429                     this.adjustAssets();
29430                 }
29431             }
29432         }
29433     },
29434
29435     // private
29436     onDrag : function(){
29437         if(!this.proxyDrag){
29438             this.xy = this.el.getXY();
29439             this.adjustAssets();
29440         }
29441     },
29442
29443     // private
29444     adjustAssets : function(doShow){
29445         var x = this.xy[0], y = this.xy[1];
29446         var w = this.size.width, h = this.size.height;
29447         if(doShow === true){
29448             if(this.shadow){
29449                 this.shadow.show(this.el);
29450             }
29451             if(this.shim){
29452                 this.shim.show();
29453             }
29454         }
29455         if(this.shadow && this.shadow.isVisible()){
29456             this.shadow.show(this.el);
29457         }
29458         if(this.shim && this.shim.isVisible()){
29459             this.shim.setBounds(x, y, w, h);
29460         }
29461     },
29462
29463     // private
29464     adjustViewport : function(w, h){
29465         if(!w || !h){
29466             w = Roo.lib.Dom.getViewWidth();
29467             h = Roo.lib.Dom.getViewHeight();
29468         }
29469         // cache the size
29470         this.viewSize = [w, h];
29471         if(this.modal && this.mask.isVisible()){
29472             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29473             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29474         }
29475         if(this.isVisible()){
29476             this.constrainXY();
29477         }
29478     },
29479
29480     /**
29481      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29482      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29483      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29484      */
29485     destroy : function(removeEl){
29486         if(this.isVisible()){
29487             this.animateTarget = null;
29488             this.hide();
29489         }
29490         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29491         if(this.tabs){
29492             this.tabs.destroy(removeEl);
29493         }
29494         Roo.destroy(
29495              this.shim,
29496              this.proxy,
29497              this.resizer,
29498              this.close,
29499              this.mask
29500         );
29501         if(this.dd){
29502             this.dd.unreg();
29503         }
29504         if(this.buttons){
29505            for(var i = 0, len = this.buttons.length; i < len; i++){
29506                this.buttons[i].destroy();
29507            }
29508         }
29509         this.el.removeAllListeners();
29510         if(removeEl === true){
29511             this.el.update("");
29512             this.el.remove();
29513         }
29514         Roo.DialogManager.unregister(this);
29515     },
29516
29517     // private
29518     startMove : function(){
29519         if(this.proxyDrag){
29520             this.proxy.show();
29521         }
29522         if(this.constraintoviewport !== false){
29523             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29524         }
29525     },
29526
29527     // private
29528     endMove : function(){
29529         if(!this.proxyDrag){
29530             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29531         }else{
29532             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29533             this.proxy.hide();
29534         }
29535         this.refreshSize();
29536         this.adjustAssets();
29537         this.focus();
29538         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29539     },
29540
29541     /**
29542      * Brings this dialog to the front of any other visible dialogs
29543      * @return {Roo.BasicDialog} this
29544      */
29545     toFront : function(){
29546         Roo.DialogManager.bringToFront(this);
29547         return this;
29548     },
29549
29550     /**
29551      * Sends this dialog to the back (under) of any other visible dialogs
29552      * @return {Roo.BasicDialog} this
29553      */
29554     toBack : function(){
29555         Roo.DialogManager.sendToBack(this);
29556         return this;
29557     },
29558
29559     /**
29560      * Centers this dialog in the viewport
29561      * @return {Roo.BasicDialog} this
29562      */
29563     center : function(){
29564         var xy = this.el.getCenterXY(true);
29565         this.moveTo(xy[0], xy[1]);
29566         return this;
29567     },
29568
29569     /**
29570      * Moves the dialog's top-left corner to the specified point
29571      * @param {Number} x
29572      * @param {Number} y
29573      * @return {Roo.BasicDialog} this
29574      */
29575     moveTo : function(x, y){
29576         this.xy = [x,y];
29577         if(this.isVisible()){
29578             this.el.setXY(this.xy);
29579             this.adjustAssets();
29580         }
29581         return this;
29582     },
29583
29584     /**
29585      * Aligns the dialog to the specified element
29586      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29587      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29588      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29589      * @return {Roo.BasicDialog} this
29590      */
29591     alignTo : function(element, position, offsets){
29592         this.xy = this.el.getAlignToXY(element, position, offsets);
29593         if(this.isVisible()){
29594             this.el.setXY(this.xy);
29595             this.adjustAssets();
29596         }
29597         return this;
29598     },
29599
29600     /**
29601      * Anchors an element to another element and realigns it when the window is resized.
29602      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29603      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29604      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29605      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29606      * is a number, it is used as the buffer delay (defaults to 50ms).
29607      * @return {Roo.BasicDialog} this
29608      */
29609     anchorTo : function(el, alignment, offsets, monitorScroll){
29610         var action = function(){
29611             this.alignTo(el, alignment, offsets);
29612         };
29613         Roo.EventManager.onWindowResize(action, this);
29614         var tm = typeof monitorScroll;
29615         if(tm != 'undefined'){
29616             Roo.EventManager.on(window, 'scroll', action, this,
29617                 {buffer: tm == 'number' ? monitorScroll : 50});
29618         }
29619         action.call(this);
29620         return this;
29621     },
29622
29623     /**
29624      * Returns true if the dialog is visible
29625      * @return {Boolean}
29626      */
29627     isVisible : function(){
29628         return this.el.isVisible();
29629     },
29630
29631     // private
29632     animHide : function(callback){
29633         var b = Roo.get(this.animateTarget).getBox();
29634         this.proxy.show();
29635         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29636         this.el.hide();
29637         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29638                     this.hideEl.createDelegate(this, [callback]));
29639     },
29640
29641     /**
29642      * Hides the dialog.
29643      * @param {Function} callback (optional) Function to call when the dialog is hidden
29644      * @return {Roo.BasicDialog} this
29645      */
29646     hide : function(callback){
29647         if (this.fireEvent("beforehide", this) === false){
29648             return;
29649         }
29650         if(this.shadow){
29651             this.shadow.hide();
29652         }
29653         if(this.shim) {
29654           this.shim.hide();
29655         }
29656         // sometimes animateTarget seems to get set.. causing problems...
29657         // this just double checks..
29658         if(this.animateTarget && Roo.get(this.animateTarget)) {
29659            this.animHide(callback);
29660         }else{
29661             this.el.hide();
29662             this.hideEl(callback);
29663         }
29664         return this;
29665     },
29666
29667     // private
29668     hideEl : function(callback){
29669         this.proxy.hide();
29670         if(this.modal){
29671             this.mask.hide();
29672             Roo.get(document.body).removeClass("x-body-masked");
29673         }
29674         this.fireEvent("hide", this);
29675         if(typeof callback == "function"){
29676             callback();
29677         }
29678     },
29679
29680     // private
29681     hideAction : function(){
29682         this.setLeft("-10000px");
29683         this.setTop("-10000px");
29684         this.setStyle("visibility", "hidden");
29685     },
29686
29687     // private
29688     refreshSize : function(){
29689         this.size = this.el.getSize();
29690         this.xy = this.el.getXY();
29691         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29692     },
29693
29694     // private
29695     // z-index is managed by the DialogManager and may be overwritten at any time
29696     setZIndex : function(index){
29697         if(this.modal){
29698             this.mask.setStyle("z-index", index);
29699         }
29700         if(this.shim){
29701             this.shim.setStyle("z-index", ++index);
29702         }
29703         if(this.shadow){
29704             this.shadow.setZIndex(++index);
29705         }
29706         this.el.setStyle("z-index", ++index);
29707         if(this.proxy){
29708             this.proxy.setStyle("z-index", ++index);
29709         }
29710         if(this.resizer){
29711             this.resizer.proxy.setStyle("z-index", ++index);
29712         }
29713
29714         this.lastZIndex = index;
29715     },
29716
29717     /**
29718      * Returns the element for this dialog
29719      * @return {Roo.Element} The underlying dialog Element
29720      */
29721     getEl : function(){
29722         return this.el;
29723     }
29724 });
29725
29726 /**
29727  * @class Roo.DialogManager
29728  * Provides global access to BasicDialogs that have been created and
29729  * support for z-indexing (layering) multiple open dialogs.
29730  */
29731 Roo.DialogManager = function(){
29732     var list = {};
29733     var accessList = [];
29734     var front = null;
29735
29736     // private
29737     var sortDialogs = function(d1, d2){
29738         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29739     };
29740
29741     // private
29742     var orderDialogs = function(){
29743         accessList.sort(sortDialogs);
29744         var seed = Roo.DialogManager.zseed;
29745         for(var i = 0, len = accessList.length; i < len; i++){
29746             var dlg = accessList[i];
29747             if(dlg){
29748                 dlg.setZIndex(seed + (i*10));
29749             }
29750         }
29751     };
29752
29753     return {
29754         /**
29755          * The starting z-index for BasicDialogs (defaults to 9000)
29756          * @type Number The z-index value
29757          */
29758         zseed : 9000,
29759
29760         // private
29761         register : function(dlg){
29762             list[dlg.id] = dlg;
29763             accessList.push(dlg);
29764         },
29765
29766         // private
29767         unregister : function(dlg){
29768             delete list[dlg.id];
29769             var i=0;
29770             var len=0;
29771             if(!accessList.indexOf){
29772                 for(  i = 0, len = accessList.length; i < len; i++){
29773                     if(accessList[i] == dlg){
29774                         accessList.splice(i, 1);
29775                         return;
29776                     }
29777                 }
29778             }else{
29779                  i = accessList.indexOf(dlg);
29780                 if(i != -1){
29781                     accessList.splice(i, 1);
29782                 }
29783             }
29784         },
29785
29786         /**
29787          * Gets a registered dialog by id
29788          * @param {String/Object} id The id of the dialog or a dialog
29789          * @return {Roo.BasicDialog} this
29790          */
29791         get : function(id){
29792             return typeof id == "object" ? id : list[id];
29793         },
29794
29795         /**
29796          * Brings the specified dialog to the front
29797          * @param {String/Object} dlg The id of the dialog or a dialog
29798          * @return {Roo.BasicDialog} this
29799          */
29800         bringToFront : function(dlg){
29801             dlg = this.get(dlg);
29802             if(dlg != front){
29803                 front = dlg;
29804                 dlg._lastAccess = new Date().getTime();
29805                 orderDialogs();
29806             }
29807             return dlg;
29808         },
29809
29810         /**
29811          * Sends the specified dialog to the back
29812          * @param {String/Object} dlg The id of the dialog or a dialog
29813          * @return {Roo.BasicDialog} this
29814          */
29815         sendToBack : function(dlg){
29816             dlg = this.get(dlg);
29817             dlg._lastAccess = -(new Date().getTime());
29818             orderDialogs();
29819             return dlg;
29820         },
29821
29822         /**
29823          * Hides all dialogs
29824          */
29825         hideAll : function(){
29826             for(var id in list){
29827                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29828                     list[id].hide();
29829                 }
29830             }
29831         }
29832     };
29833 }();
29834
29835 /**
29836  * @class Roo.LayoutDialog
29837  * @extends Roo.BasicDialog
29838  * Dialog which provides adjustments for working with a layout in a Dialog.
29839  * Add your necessary layout config options to the dialog's config.<br>
29840  * Example usage (including a nested layout):
29841  * <pre><code>
29842 if(!dialog){
29843     dialog = new Roo.LayoutDialog("download-dlg", {
29844         modal: true,
29845         width:600,
29846         height:450,
29847         shadow:true,
29848         minWidth:500,
29849         minHeight:350,
29850         autoTabs:true,
29851         proxyDrag:true,
29852         // layout config merges with the dialog config
29853         center:{
29854             tabPosition: "top",
29855             alwaysShowTabs: true
29856         }
29857     });
29858     dialog.addKeyListener(27, dialog.hide, dialog);
29859     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29860     dialog.addButton("Build It!", this.getDownload, this);
29861
29862     // we can even add nested layouts
29863     var innerLayout = new Roo.BorderLayout("dl-inner", {
29864         east: {
29865             initialSize: 200,
29866             autoScroll:true,
29867             split:true
29868         },
29869         center: {
29870             autoScroll:true
29871         }
29872     });
29873     innerLayout.beginUpdate();
29874     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29875     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29876     innerLayout.endUpdate(true);
29877
29878     var layout = dialog.getLayout();
29879     layout.beginUpdate();
29880     layout.add("center", new Roo.ContentPanel("standard-panel",
29881                         {title: "Download the Source", fitToFrame:true}));
29882     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29883                {title: "Build your own roo.js"}));
29884     layout.getRegion("center").showPanel(sp);
29885     layout.endUpdate();
29886 }
29887 </code></pre>
29888     * @constructor
29889     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29890     * @param {Object} config configuration options
29891   */
29892 Roo.LayoutDialog = function(el, cfg){
29893     
29894     var config=  cfg;
29895     if (typeof(cfg) == 'undefined') {
29896         config = Roo.apply({}, el);
29897         // not sure why we use documentElement here.. - it should always be body.
29898         // IE7 borks horribly if we use documentElement.
29899         // webkit also does not like documentElement - it creates a body element...
29900         el = Roo.get( document.body || document.documentElement ).createChild();
29901         //config.autoCreate = true;
29902     }
29903     
29904     
29905     config.autoTabs = false;
29906     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29907     this.body.setStyle({overflow:"hidden", position:"relative"});
29908     this.layout = new Roo.BorderLayout(this.body.dom, config);
29909     this.layout.monitorWindowResize = false;
29910     this.el.addClass("x-dlg-auto-layout");
29911     // fix case when center region overwrites center function
29912     this.center = Roo.BasicDialog.prototype.center;
29913     this.on("show", this.layout.layout, this.layout, true);
29914     if (config.items) {
29915         var xitems = config.items;
29916         delete config.items;
29917         Roo.each(xitems, this.addxtype, this);
29918     }
29919     
29920     
29921 };
29922 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29923     /**
29924      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29925      * @deprecated
29926      */
29927     endUpdate : function(){
29928         this.layout.endUpdate();
29929     },
29930
29931     /**
29932      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29933      *  @deprecated
29934      */
29935     beginUpdate : function(){
29936         this.layout.beginUpdate();
29937     },
29938
29939     /**
29940      * Get the BorderLayout for this dialog
29941      * @return {Roo.BorderLayout}
29942      */
29943     getLayout : function(){
29944         return this.layout;
29945     },
29946
29947     showEl : function(){
29948         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29949         if(Roo.isIE7){
29950             this.layout.layout();
29951         }
29952     },
29953
29954     // private
29955     // Use the syncHeightBeforeShow config option to control this automatically
29956     syncBodyHeight : function(){
29957         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29958         if(this.layout){this.layout.layout();}
29959     },
29960     
29961       /**
29962      * Add an xtype element (actually adds to the layout.)
29963      * @return {Object} xdata xtype object data.
29964      */
29965     
29966     addxtype : function(c) {
29967         return this.layout.addxtype(c);
29968     }
29969 });/*
29970  * Based on:
29971  * Ext JS Library 1.1.1
29972  * Copyright(c) 2006-2007, Ext JS, LLC.
29973  *
29974  * Originally Released Under LGPL - original licence link has changed is not relivant.
29975  *
29976  * Fork - LGPL
29977  * <script type="text/javascript">
29978  */
29979  
29980 /**
29981  * @class Roo.MessageBox
29982  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29983  * Example usage:
29984  *<pre><code>
29985 // Basic alert:
29986 Roo.Msg.alert('Status', 'Changes saved successfully.');
29987
29988 // Prompt for user data:
29989 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29990     if (btn == 'ok'){
29991         // process text value...
29992     }
29993 });
29994
29995 // Show a dialog using config options:
29996 Roo.Msg.show({
29997    title:'Save Changes?',
29998    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29999    buttons: Roo.Msg.YESNOCANCEL,
30000    fn: processResult,
30001    animEl: 'elId'
30002 });
30003 </code></pre>
30004  * @singleton
30005  */
30006 Roo.MessageBox = function(){
30007     var dlg, opt, mask, waitTimer;
30008     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
30009     var buttons, activeTextEl, bwidth;
30010
30011     // private
30012     var handleButton = function(button){
30013         dlg.hide();
30014         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
30015     };
30016
30017     // private
30018     var handleHide = function(){
30019         if(opt && opt.cls){
30020             dlg.el.removeClass(opt.cls);
30021         }
30022         if(waitTimer){
30023             Roo.TaskMgr.stop(waitTimer);
30024             waitTimer = null;
30025         }
30026     };
30027
30028     // private
30029     var updateButtons = function(b){
30030         var width = 0;
30031         if(!b){
30032             buttons["ok"].hide();
30033             buttons["cancel"].hide();
30034             buttons["yes"].hide();
30035             buttons["no"].hide();
30036             dlg.footer.dom.style.display = 'none';
30037             return width;
30038         }
30039         dlg.footer.dom.style.display = '';
30040         for(var k in buttons){
30041             if(typeof buttons[k] != "function"){
30042                 if(b[k]){
30043                     buttons[k].show();
30044                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30045                     width += buttons[k].el.getWidth()+15;
30046                 }else{
30047                     buttons[k].hide();
30048                 }
30049             }
30050         }
30051         return width;
30052     };
30053
30054     // private
30055     var handleEsc = function(d, k, e){
30056         if(opt && opt.closable !== false){
30057             dlg.hide();
30058         }
30059         if(e){
30060             e.stopEvent();
30061         }
30062     };
30063
30064     return {
30065         /**
30066          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30067          * @return {Roo.BasicDialog} The BasicDialog element
30068          */
30069         getDialog : function(){
30070            if(!dlg){
30071                 dlg = new Roo.BasicDialog("x-msg-box", {
30072                     autoCreate : true,
30073                     shadow: true,
30074                     draggable: true,
30075                     resizable:false,
30076                     constraintoviewport:false,
30077                     fixedcenter:true,
30078                     collapsible : false,
30079                     shim:true,
30080                     modal: true,
30081                     width:400, height:100,
30082                     buttonAlign:"center",
30083                     closeClick : function(){
30084                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30085                             handleButton("no");
30086                         }else{
30087                             handleButton("cancel");
30088                         }
30089                     }
30090                 });
30091                 dlg.on("hide", handleHide);
30092                 mask = dlg.mask;
30093                 dlg.addKeyListener(27, handleEsc);
30094                 buttons = {};
30095                 var bt = this.buttonText;
30096                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30097                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30098                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30099                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30100                 bodyEl = dlg.body.createChild({
30101
30102                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
30103                 });
30104                 msgEl = bodyEl.dom.firstChild;
30105                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30106                 textboxEl.enableDisplayMode();
30107                 textboxEl.addKeyListener([10,13], function(){
30108                     if(dlg.isVisible() && opt && opt.buttons){
30109                         if(opt.buttons.ok){
30110                             handleButton("ok");
30111                         }else if(opt.buttons.yes){
30112                             handleButton("yes");
30113                         }
30114                     }
30115                 });
30116                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30117                 textareaEl.enableDisplayMode();
30118                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30119                 progressEl.enableDisplayMode();
30120                 var pf = progressEl.dom.firstChild;
30121                 if (pf) {
30122                     pp = Roo.get(pf.firstChild);
30123                     pp.setHeight(pf.offsetHeight);
30124                 }
30125                 
30126             }
30127             return dlg;
30128         },
30129
30130         /**
30131          * Updates the message box body text
30132          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30133          * the XHTML-compliant non-breaking space character '&amp;#160;')
30134          * @return {Roo.MessageBox} This message box
30135          */
30136         updateText : function(text){
30137             if(!dlg.isVisible() && !opt.width){
30138                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30139             }
30140             msgEl.innerHTML = text || '&#160;';
30141             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
30142                         Math.max(opt.minWidth || this.minWidth, bwidth));
30143             if(opt.prompt){
30144                 activeTextEl.setWidth(w);
30145             }
30146             if(dlg.isVisible()){
30147                 dlg.fixedcenter = false;
30148             }
30149             // to big, make it scoll.
30150             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
30151                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
30152                 bodyEl.dom.style.overflowY = 'auto';
30153             } else {
30154                 bodyEl.dom.style.height = '';
30155                 bodyEl.dom.style.overflowY = '';
30156             }
30157             
30158             dlg.setContentSize(w, bodyEl.getHeight());
30159             if(dlg.isVisible()){
30160                 dlg.fixedcenter = true;
30161             }
30162             return this;
30163         },
30164
30165         /**
30166          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30167          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30168          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30169          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30170          * @return {Roo.MessageBox} This message box
30171          */
30172         updateProgress : function(value, text){
30173             if(text){
30174                 this.updateText(text);
30175             }
30176             if (pp) { // weird bug on my firefox - for some reason this is not defined
30177                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30178             }
30179             return this;
30180         },        
30181
30182         /**
30183          * Returns true if the message box is currently displayed
30184          * @return {Boolean} True if the message box is visible, else false
30185          */
30186         isVisible : function(){
30187             return dlg && dlg.isVisible();  
30188         },
30189
30190         /**
30191          * Hides the message box if it is displayed
30192          */
30193         hide : function(){
30194             if(this.isVisible()){
30195                 dlg.hide();
30196             }  
30197         },
30198
30199         /**
30200          * Displays a new message box, or reinitializes an existing message box, based on the config options
30201          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30202          * The following config object properties are supported:
30203          * <pre>
30204 Property    Type             Description
30205 ----------  ---------------  ------------------------------------------------------------------------------------
30206 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30207                                    closes (defaults to undefined)
30208 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30209                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30210 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30211                                    progress and wait dialogs will ignore this property and always hide the
30212                                    close button as they can only be closed programmatically.
30213 cls               String           A custom CSS class to apply to the message box element
30214 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30215                                    displayed (defaults to 75)
30216 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30217                                    function will be btn (the name of the button that was clicked, if applicable,
30218                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30219                                    Progress and wait dialogs will ignore this option since they do not respond to
30220                                    user actions and can only be closed programmatically, so any required function
30221                                    should be called by the same code after it closes the dialog.
30222 icon              String           A CSS class that provides a background image to be used as an icon for
30223                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30224 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30225 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30226 modal             Boolean          False to allow user interaction with the page while the message box is
30227                                    displayed (defaults to true)
30228 msg               String           A string that will replace the existing message box body text (defaults
30229                                    to the XHTML-compliant non-breaking space character '&#160;')
30230 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30231 progress          Boolean          True to display a progress bar (defaults to false)
30232 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30233 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30234 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30235 title             String           The title text
30236 value             String           The string value to set into the active textbox element if displayed
30237 wait              Boolean          True to display a progress bar (defaults to false)
30238 width             Number           The width of the dialog in pixels
30239 </pre>
30240          *
30241          * Example usage:
30242          * <pre><code>
30243 Roo.Msg.show({
30244    title: 'Address',
30245    msg: 'Please enter your address:',
30246    width: 300,
30247    buttons: Roo.MessageBox.OKCANCEL,
30248    multiline: true,
30249    fn: saveAddress,
30250    animEl: 'addAddressBtn'
30251 });
30252 </code></pre>
30253          * @param {Object} config Configuration options
30254          * @return {Roo.MessageBox} This message box
30255          */
30256         show : function(options)
30257         {
30258             
30259             // this causes nightmares if you show one dialog after another
30260             // especially on callbacks..
30261              
30262             if(this.isVisible()){
30263                 
30264                 this.hide();
30265                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML )
30266                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30267                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30268                 
30269             }
30270             var d = this.getDialog();
30271             opt = options;
30272             d.setTitle(opt.title || "&#160;");
30273             d.close.setDisplayed(opt.closable !== false);
30274             activeTextEl = textboxEl;
30275             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30276             if(opt.prompt){
30277                 if(opt.multiline){
30278                     textboxEl.hide();
30279                     textareaEl.show();
30280                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30281                         opt.multiline : this.defaultTextHeight);
30282                     activeTextEl = textareaEl;
30283                 }else{
30284                     textboxEl.show();
30285                     textareaEl.hide();
30286                 }
30287             }else{
30288                 textboxEl.hide();
30289                 textareaEl.hide();
30290             }
30291             progressEl.setDisplayed(opt.progress === true);
30292             this.updateProgress(0);
30293             activeTextEl.dom.value = opt.value || "";
30294             if(opt.prompt){
30295                 dlg.setDefaultButton(activeTextEl);
30296             }else{
30297                 var bs = opt.buttons;
30298                 var db = null;
30299                 if(bs && bs.ok){
30300                     db = buttons["ok"];
30301                 }else if(bs && bs.yes){
30302                     db = buttons["yes"];
30303                 }
30304                 dlg.setDefaultButton(db);
30305             }
30306             bwidth = updateButtons(opt.buttons);
30307             this.updateText(opt.msg);
30308             if(opt.cls){
30309                 d.el.addClass(opt.cls);
30310             }
30311             d.proxyDrag = opt.proxyDrag === true;
30312             d.modal = opt.modal !== false;
30313             d.mask = opt.modal !== false ? mask : false;
30314             if(!d.isVisible()){
30315                 // force it to the end of the z-index stack so it gets a cursor in FF
30316                 document.body.appendChild(dlg.el.dom);
30317                 d.animateTarget = null;
30318                 d.show(options.animEl);
30319             }
30320             return this;
30321         },
30322
30323         /**
30324          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30325          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30326          * and closing the message box when the process is complete.
30327          * @param {String} title The title bar text
30328          * @param {String} msg The message box body text
30329          * @return {Roo.MessageBox} This message box
30330          */
30331         progress : function(title, msg){
30332             this.show({
30333                 title : title,
30334                 msg : msg,
30335                 buttons: false,
30336                 progress:true,
30337                 closable:false,
30338                 minWidth: this.minProgressWidth,
30339                 modal : true
30340             });
30341             return this;
30342         },
30343
30344         /**
30345          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30346          * If a callback function is passed it will be called after the user clicks the button, and the
30347          * id of the button that was clicked will be passed as the only parameter to the callback
30348          * (could also be the top-right close button).
30349          * @param {String} title The title bar text
30350          * @param {String} msg The message box body text
30351          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30352          * @param {Object} scope (optional) The scope of the callback function
30353          * @return {Roo.MessageBox} This message box
30354          */
30355         alert : function(title, msg, fn, scope){
30356             this.show({
30357                 title : title,
30358                 msg : msg,
30359                 buttons: this.OK,
30360                 fn: fn,
30361                 scope : scope,
30362                 modal : true
30363             });
30364             return this;
30365         },
30366
30367         /**
30368          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30369          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30370          * You are responsible for closing the message box when the process is complete.
30371          * @param {String} msg The message box body text
30372          * @param {String} title (optional) The title bar text
30373          * @return {Roo.MessageBox} This message box
30374          */
30375         wait : function(msg, title){
30376             this.show({
30377                 title : title,
30378                 msg : msg,
30379                 buttons: false,
30380                 closable:false,
30381                 progress:true,
30382                 modal:true,
30383                 width:300,
30384                 wait:true
30385             });
30386             waitTimer = Roo.TaskMgr.start({
30387                 run: function(i){
30388                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30389                 },
30390                 interval: 1000
30391             });
30392             return this;
30393         },
30394
30395         /**
30396          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30397          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30398          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30399          * @param {String} title The title bar text
30400          * @param {String} msg The message box body text
30401          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30402          * @param {Object} scope (optional) The scope of the callback function
30403          * @return {Roo.MessageBox} This message box
30404          */
30405         confirm : function(title, msg, fn, scope){
30406             this.show({
30407                 title : title,
30408                 msg : msg,
30409                 buttons: this.YESNO,
30410                 fn: fn,
30411                 scope : scope,
30412                 modal : true
30413             });
30414             return this;
30415         },
30416
30417         /**
30418          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30419          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30420          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30421          * (could also be the top-right close button) and the text that was entered will be passed as the two
30422          * parameters to the callback.
30423          * @param {String} title The title bar text
30424          * @param {String} msg The message box body text
30425          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30426          * @param {Object} scope (optional) The scope of the callback function
30427          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30428          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30429          * @return {Roo.MessageBox} This message box
30430          */
30431         prompt : function(title, msg, fn, scope, multiline){
30432             this.show({
30433                 title : title,
30434                 msg : msg,
30435                 buttons: this.OKCANCEL,
30436                 fn: fn,
30437                 minWidth:250,
30438                 scope : scope,
30439                 prompt:true,
30440                 multiline: multiline,
30441                 modal : true
30442             });
30443             return this;
30444         },
30445
30446         /**
30447          * Button config that displays a single OK button
30448          * @type Object
30449          */
30450         OK : {ok:true},
30451         /**
30452          * Button config that displays Yes and No buttons
30453          * @type Object
30454          */
30455         YESNO : {yes:true, no:true},
30456         /**
30457          * Button config that displays OK and Cancel buttons
30458          * @type Object
30459          */
30460         OKCANCEL : {ok:true, cancel:true},
30461         /**
30462          * Button config that displays Yes, No and Cancel buttons
30463          * @type Object
30464          */
30465         YESNOCANCEL : {yes:true, no:true, cancel:true},
30466
30467         /**
30468          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30469          * @type Number
30470          */
30471         defaultTextHeight : 75,
30472         /**
30473          * The maximum width in pixels of the message box (defaults to 600)
30474          * @type Number
30475          */
30476         maxWidth : 600,
30477         /**
30478          * The minimum width in pixels of the message box (defaults to 100)
30479          * @type Number
30480          */
30481         minWidth : 100,
30482         /**
30483          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30484          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30485          * @type Number
30486          */
30487         minProgressWidth : 250,
30488         /**
30489          * An object containing the default button text strings that can be overriden for localized language support.
30490          * Supported properties are: ok, cancel, yes and no.
30491          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30492          * @type Object
30493          */
30494         buttonText : {
30495             ok : "OK",
30496             cancel : "Cancel",
30497             yes : "Yes",
30498             no : "No"
30499         }
30500     };
30501 }();
30502
30503 /**
30504  * Shorthand for {@link Roo.MessageBox}
30505  */
30506 Roo.Msg = Roo.MessageBox;/*
30507  * Based on:
30508  * Ext JS Library 1.1.1
30509  * Copyright(c) 2006-2007, Ext JS, LLC.
30510  *
30511  * Originally Released Under LGPL - original licence link has changed is not relivant.
30512  *
30513  * Fork - LGPL
30514  * <script type="text/javascript">
30515  */
30516 /**
30517  * @class Roo.QuickTips
30518  * Provides attractive and customizable tooltips for any element.
30519  * @singleton
30520  */
30521 Roo.QuickTips = function(){
30522     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30523     var ce, bd, xy, dd;
30524     var visible = false, disabled = true, inited = false;
30525     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30526     
30527     var onOver = function(e){
30528         if(disabled){
30529             return;
30530         }
30531         var t = e.getTarget();
30532         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30533             return;
30534         }
30535         if(ce && t == ce.el){
30536             clearTimeout(hideProc);
30537             return;
30538         }
30539         if(t && tagEls[t.id]){
30540             tagEls[t.id].el = t;
30541             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30542             return;
30543         }
30544         var ttp, et = Roo.fly(t);
30545         var ns = cfg.namespace;
30546         if(tm.interceptTitles && t.title){
30547             ttp = t.title;
30548             t.qtip = ttp;
30549             t.removeAttribute("title");
30550             e.preventDefault();
30551         }else{
30552             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30553         }
30554         if(ttp){
30555             showProc = show.defer(tm.showDelay, tm, [{
30556                 el: t, 
30557                 text: ttp, 
30558                 width: et.getAttributeNS(ns, cfg.width),
30559                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30560                 title: et.getAttributeNS(ns, cfg.title),
30561                     cls: et.getAttributeNS(ns, cfg.cls)
30562             }]);
30563         }
30564     };
30565     
30566     var onOut = function(e){
30567         clearTimeout(showProc);
30568         var t = e.getTarget();
30569         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30570             hideProc = setTimeout(hide, tm.hideDelay);
30571         }
30572     };
30573     
30574     var onMove = function(e){
30575         if(disabled){
30576             return;
30577         }
30578         xy = e.getXY();
30579         xy[1] += 18;
30580         if(tm.trackMouse && ce){
30581             el.setXY(xy);
30582         }
30583     };
30584     
30585     var onDown = function(e){
30586         clearTimeout(showProc);
30587         clearTimeout(hideProc);
30588         if(!e.within(el)){
30589             if(tm.hideOnClick){
30590                 hide();
30591                 tm.disable();
30592                 tm.enable.defer(100, tm);
30593             }
30594         }
30595     };
30596     
30597     var getPad = function(){
30598         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30599     };
30600
30601     var show = function(o){
30602         if(disabled){
30603             return;
30604         }
30605         clearTimeout(dismissProc);
30606         ce = o;
30607         if(removeCls){ // in case manually hidden
30608             el.removeClass(removeCls);
30609             removeCls = null;
30610         }
30611         if(ce.cls){
30612             el.addClass(ce.cls);
30613             removeCls = ce.cls;
30614         }
30615         if(ce.title){
30616             tipTitle.update(ce.title);
30617             tipTitle.show();
30618         }else{
30619             tipTitle.update('');
30620             tipTitle.hide();
30621         }
30622         el.dom.style.width  = tm.maxWidth+'px';
30623         //tipBody.dom.style.width = '';
30624         tipBodyText.update(o.text);
30625         var p = getPad(), w = ce.width;
30626         if(!w){
30627             var td = tipBodyText.dom;
30628             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30629             if(aw > tm.maxWidth){
30630                 w = tm.maxWidth;
30631             }else if(aw < tm.minWidth){
30632                 w = tm.minWidth;
30633             }else{
30634                 w = aw;
30635             }
30636         }
30637         //tipBody.setWidth(w);
30638         el.setWidth(parseInt(w, 10) + p);
30639         if(ce.autoHide === false){
30640             close.setDisplayed(true);
30641             if(dd){
30642                 dd.unlock();
30643             }
30644         }else{
30645             close.setDisplayed(false);
30646             if(dd){
30647                 dd.lock();
30648             }
30649         }
30650         if(xy){
30651             el.avoidY = xy[1]-18;
30652             el.setXY(xy);
30653         }
30654         if(tm.animate){
30655             el.setOpacity(.1);
30656             el.setStyle("visibility", "visible");
30657             el.fadeIn({callback: afterShow});
30658         }else{
30659             afterShow();
30660         }
30661     };
30662     
30663     var afterShow = function(){
30664         if(ce){
30665             el.show();
30666             esc.enable();
30667             if(tm.autoDismiss && ce.autoHide !== false){
30668                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30669             }
30670         }
30671     };
30672     
30673     var hide = function(noanim){
30674         clearTimeout(dismissProc);
30675         clearTimeout(hideProc);
30676         ce = null;
30677         if(el.isVisible()){
30678             esc.disable();
30679             if(noanim !== true && tm.animate){
30680                 el.fadeOut({callback: afterHide});
30681             }else{
30682                 afterHide();
30683             } 
30684         }
30685     };
30686     
30687     var afterHide = function(){
30688         el.hide();
30689         if(removeCls){
30690             el.removeClass(removeCls);
30691             removeCls = null;
30692         }
30693     };
30694     
30695     return {
30696         /**
30697         * @cfg {Number} minWidth
30698         * The minimum width of the quick tip (defaults to 40)
30699         */
30700        minWidth : 40,
30701         /**
30702         * @cfg {Number} maxWidth
30703         * The maximum width of the quick tip (defaults to 300)
30704         */
30705        maxWidth : 300,
30706         /**
30707         * @cfg {Boolean} interceptTitles
30708         * True to automatically use the element's DOM title value if available (defaults to false)
30709         */
30710        interceptTitles : false,
30711         /**
30712         * @cfg {Boolean} trackMouse
30713         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30714         */
30715        trackMouse : false,
30716         /**
30717         * @cfg {Boolean} hideOnClick
30718         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30719         */
30720        hideOnClick : true,
30721         /**
30722         * @cfg {Number} showDelay
30723         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30724         */
30725        showDelay : 500,
30726         /**
30727         * @cfg {Number} hideDelay
30728         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30729         */
30730        hideDelay : 200,
30731         /**
30732         * @cfg {Boolean} autoHide
30733         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30734         * Used in conjunction with hideDelay.
30735         */
30736        autoHide : true,
30737         /**
30738         * @cfg {Boolean}
30739         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30740         * (defaults to true).  Used in conjunction with autoDismissDelay.
30741         */
30742        autoDismiss : true,
30743         /**
30744         * @cfg {Number}
30745         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30746         */
30747        autoDismissDelay : 5000,
30748        /**
30749         * @cfg {Boolean} animate
30750         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30751         */
30752        animate : false,
30753
30754        /**
30755         * @cfg {String} title
30756         * Title text to display (defaults to '').  This can be any valid HTML markup.
30757         */
30758         title: '',
30759        /**
30760         * @cfg {String} text
30761         * Body text to display (defaults to '').  This can be any valid HTML markup.
30762         */
30763         text : '',
30764        /**
30765         * @cfg {String} cls
30766         * A CSS class to apply to the base quick tip element (defaults to '').
30767         */
30768         cls : '',
30769        /**
30770         * @cfg {Number} width
30771         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30772         * minWidth or maxWidth.
30773         */
30774         width : null,
30775
30776     /**
30777      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30778      * or display QuickTips in a page.
30779      */
30780        init : function(){
30781           tm = Roo.QuickTips;
30782           cfg = tm.tagConfig;
30783           if(!inited){
30784               if(!Roo.isReady){ // allow calling of init() before onReady
30785                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30786                   return;
30787               }
30788               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30789               el.fxDefaults = {stopFx: true};
30790               // maximum custom styling
30791               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
30792               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
30793               tipTitle = el.child('h3');
30794               tipTitle.enableDisplayMode("block");
30795               tipBody = el.child('div.x-tip-bd');
30796               tipBodyText = el.child('div.x-tip-bd-inner');
30797               //bdLeft = el.child('div.x-tip-bd-left');
30798               //bdRight = el.child('div.x-tip-bd-right');
30799               close = el.child('div.x-tip-close');
30800               close.enableDisplayMode("block");
30801               close.on("click", hide);
30802               var d = Roo.get(document);
30803               d.on("mousedown", onDown);
30804               d.on("mouseover", onOver);
30805               d.on("mouseout", onOut);
30806               d.on("mousemove", onMove);
30807               esc = d.addKeyListener(27, hide);
30808               esc.disable();
30809               if(Roo.dd.DD){
30810                   dd = el.initDD("default", null, {
30811                       onDrag : function(){
30812                           el.sync();  
30813                       }
30814                   });
30815                   dd.setHandleElId(tipTitle.id);
30816                   dd.lock();
30817               }
30818               inited = true;
30819           }
30820           this.enable(); 
30821        },
30822
30823     /**
30824      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30825      * are supported:
30826      * <pre>
30827 Property    Type                   Description
30828 ----------  ---------------------  ------------------------------------------------------------------------
30829 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30830      * </ul>
30831      * @param {Object} config The config object
30832      */
30833        register : function(config){
30834            var cs = config instanceof Array ? config : arguments;
30835            for(var i = 0, len = cs.length; i < len; i++) {
30836                var c = cs[i];
30837                var target = c.target;
30838                if(target){
30839                    if(target instanceof Array){
30840                        for(var j = 0, jlen = target.length; j < jlen; j++){
30841                            tagEls[target[j]] = c;
30842                        }
30843                    }else{
30844                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30845                    }
30846                }
30847            }
30848        },
30849
30850     /**
30851      * Removes this quick tip from its element and destroys it.
30852      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30853      */
30854        unregister : function(el){
30855            delete tagEls[Roo.id(el)];
30856        },
30857
30858     /**
30859      * Enable this quick tip.
30860      */
30861        enable : function(){
30862            if(inited && disabled){
30863                locks.pop();
30864                if(locks.length < 1){
30865                    disabled = false;
30866                }
30867            }
30868        },
30869
30870     /**
30871      * Disable this quick tip.
30872      */
30873        disable : function(){
30874           disabled = true;
30875           clearTimeout(showProc);
30876           clearTimeout(hideProc);
30877           clearTimeout(dismissProc);
30878           if(ce){
30879               hide(true);
30880           }
30881           locks.push(1);
30882        },
30883
30884     /**
30885      * Returns true if the quick tip is enabled, else false.
30886      */
30887        isEnabled : function(){
30888             return !disabled;
30889        },
30890
30891         // private
30892        tagConfig : {
30893            namespace : "ext",
30894            attribute : "qtip",
30895            width : "width",
30896            target : "target",
30897            title : "qtitle",
30898            hide : "hide",
30899            cls : "qclass"
30900        }
30901    };
30902 }();
30903
30904 // backwards compat
30905 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30906  * Based on:
30907  * Ext JS Library 1.1.1
30908  * Copyright(c) 2006-2007, Ext JS, LLC.
30909  *
30910  * Originally Released Under LGPL - original licence link has changed is not relivant.
30911  *
30912  * Fork - LGPL
30913  * <script type="text/javascript">
30914  */
30915  
30916
30917 /**
30918  * @class Roo.tree.TreePanel
30919  * @extends Roo.data.Tree
30920
30921  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30922  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30923  * @cfg {Boolean} enableDD true to enable drag and drop
30924  * @cfg {Boolean} enableDrag true to enable just drag
30925  * @cfg {Boolean} enableDrop true to enable just drop
30926  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30927  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30928  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30929  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30930  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30931  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30932  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30933  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30934  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30935  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30936  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30937  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30938  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
30939  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30940  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30941  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30942  * 
30943  * @constructor
30944  * @param {String/HTMLElement/Element} el The container element
30945  * @param {Object} config
30946  */
30947 Roo.tree.TreePanel = function(el, config){
30948     var root = false;
30949     var loader = false;
30950     if (config.root) {
30951         root = config.root;
30952         delete config.root;
30953     }
30954     if (config.loader) {
30955         loader = config.loader;
30956         delete config.loader;
30957     }
30958     
30959     Roo.apply(this, config);
30960     Roo.tree.TreePanel.superclass.constructor.call(this);
30961     this.el = Roo.get(el);
30962     this.el.addClass('x-tree');
30963     //console.log(root);
30964     if (root) {
30965         this.setRootNode( Roo.factory(root, Roo.tree));
30966     }
30967     if (loader) {
30968         this.loader = Roo.factory(loader, Roo.tree);
30969     }
30970    /**
30971     * Read-only. The id of the container element becomes this TreePanel's id.
30972     */
30973     this.id = this.el.id;
30974     this.addEvents({
30975         /**
30976         * @event beforeload
30977         * Fires before a node is loaded, return false to cancel
30978         * @param {Node} node The node being loaded
30979         */
30980         "beforeload" : true,
30981         /**
30982         * @event load
30983         * Fires when a node is loaded
30984         * @param {Node} node The node that was loaded
30985         */
30986         "load" : true,
30987         /**
30988         * @event textchange
30989         * Fires when the text for a node is changed
30990         * @param {Node} node The node
30991         * @param {String} text The new text
30992         * @param {String} oldText The old text
30993         */
30994         "textchange" : true,
30995         /**
30996         * @event beforeexpand
30997         * Fires before a node is expanded, return false to cancel.
30998         * @param {Node} node The node
30999         * @param {Boolean} deep
31000         * @param {Boolean} anim
31001         */
31002         "beforeexpand" : true,
31003         /**
31004         * @event beforecollapse
31005         * Fires before a node is collapsed, return false to cancel.
31006         * @param {Node} node The node
31007         * @param {Boolean} deep
31008         * @param {Boolean} anim
31009         */
31010         "beforecollapse" : true,
31011         /**
31012         * @event expand
31013         * Fires when a node is expanded
31014         * @param {Node} node The node
31015         */
31016         "expand" : true,
31017         /**
31018         * @event disabledchange
31019         * Fires when the disabled status of a node changes
31020         * @param {Node} node The node
31021         * @param {Boolean} disabled
31022         */
31023         "disabledchange" : true,
31024         /**
31025         * @event collapse
31026         * Fires when a node is collapsed
31027         * @param {Node} node The node
31028         */
31029         "collapse" : true,
31030         /**
31031         * @event beforeclick
31032         * Fires before click processing on a node. Return false to cancel the default action.
31033         * @param {Node} node The node
31034         * @param {Roo.EventObject} e The event object
31035         */
31036         "beforeclick":true,
31037         /**
31038         * @event checkchange
31039         * Fires when a node with a checkbox's checked property changes
31040         * @param {Node} this This node
31041         * @param {Boolean} checked
31042         */
31043         "checkchange":true,
31044         /**
31045         * @event click
31046         * Fires when a node is clicked
31047         * @param {Node} node The node
31048         * @param {Roo.EventObject} e The event object
31049         */
31050         "click":true,
31051         /**
31052         * @event dblclick
31053         * Fires when a node is double clicked
31054         * @param {Node} node The node
31055         * @param {Roo.EventObject} e The event object
31056         */
31057         "dblclick":true,
31058         /**
31059         * @event contextmenu
31060         * Fires when a node is right clicked
31061         * @param {Node} node The node
31062         * @param {Roo.EventObject} e The event object
31063         */
31064         "contextmenu":true,
31065         /**
31066         * @event beforechildrenrendered
31067         * Fires right before the child nodes for a node are rendered
31068         * @param {Node} node The node
31069         */
31070         "beforechildrenrendered":true,
31071         /**
31072         * @event startdrag
31073         * Fires when a node starts being dragged
31074         * @param {Roo.tree.TreePanel} this
31075         * @param {Roo.tree.TreeNode} node
31076         * @param {event} e The raw browser event
31077         */ 
31078        "startdrag" : true,
31079        /**
31080         * @event enddrag
31081         * Fires when a drag operation is complete
31082         * @param {Roo.tree.TreePanel} this
31083         * @param {Roo.tree.TreeNode} node
31084         * @param {event} e The raw browser event
31085         */
31086        "enddrag" : true,
31087        /**
31088         * @event dragdrop
31089         * Fires when a dragged node is dropped on a valid DD target
31090         * @param {Roo.tree.TreePanel} this
31091         * @param {Roo.tree.TreeNode} node
31092         * @param {DD} dd The dd it was dropped on
31093         * @param {event} e The raw browser event
31094         */
31095        "dragdrop" : true,
31096        /**
31097         * @event beforenodedrop
31098         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31099         * passed to handlers has the following properties:<br />
31100         * <ul style="padding:5px;padding-left:16px;">
31101         * <li>tree - The TreePanel</li>
31102         * <li>target - The node being targeted for the drop</li>
31103         * <li>data - The drag data from the drag source</li>
31104         * <li>point - The point of the drop - append, above or below</li>
31105         * <li>source - The drag source</li>
31106         * <li>rawEvent - Raw mouse event</li>
31107         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31108         * to be inserted by setting them on this object.</li>
31109         * <li>cancel - Set this to true to cancel the drop.</li>
31110         * </ul>
31111         * @param {Object} dropEvent
31112         */
31113        "beforenodedrop" : true,
31114        /**
31115         * @event nodedrop
31116         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31117         * passed to handlers has the following properties:<br />
31118         * <ul style="padding:5px;padding-left:16px;">
31119         * <li>tree - The TreePanel</li>
31120         * <li>target - The node being targeted for the drop</li>
31121         * <li>data - The drag data from the drag source</li>
31122         * <li>point - The point of the drop - append, above or below</li>
31123         * <li>source - The drag source</li>
31124         * <li>rawEvent - Raw mouse event</li>
31125         * <li>dropNode - Dropped node(s).</li>
31126         * </ul>
31127         * @param {Object} dropEvent
31128         */
31129        "nodedrop" : true,
31130         /**
31131         * @event nodedragover
31132         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31133         * passed to handlers has the following properties:<br />
31134         * <ul style="padding:5px;padding-left:16px;">
31135         * <li>tree - The TreePanel</li>
31136         * <li>target - The node being targeted for the drop</li>
31137         * <li>data - The drag data from the drag source</li>
31138         * <li>point - The point of the drop - append, above or below</li>
31139         * <li>source - The drag source</li>
31140         * <li>rawEvent - Raw mouse event</li>
31141         * <li>dropNode - Drop node(s) provided by the source.</li>
31142         * <li>cancel - Set this to true to signal drop not allowed.</li>
31143         * </ul>
31144         * @param {Object} dragOverEvent
31145         */
31146        "nodedragover" : true
31147         
31148     });
31149     if(this.singleExpand){
31150        this.on("beforeexpand", this.restrictExpand, this);
31151     }
31152     if (this.editor) {
31153         this.editor.tree = this;
31154         this.editor = Roo.factory(this.editor, Roo.tree);
31155     }
31156     
31157     if (this.selModel) {
31158         this.selModel = Roo.factory(this.selModel, Roo.tree);
31159     }
31160    
31161 };
31162 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31163     rootVisible : true,
31164     animate: Roo.enableFx,
31165     lines : true,
31166     enableDD : false,
31167     hlDrop : Roo.enableFx,
31168   
31169     renderer: false,
31170     
31171     rendererTip: false,
31172     // private
31173     restrictExpand : function(node){
31174         var p = node.parentNode;
31175         if(p){
31176             if(p.expandedChild && p.expandedChild.parentNode == p){
31177                 p.expandedChild.collapse();
31178             }
31179             p.expandedChild = node;
31180         }
31181     },
31182
31183     // private override
31184     setRootNode : function(node){
31185         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31186         if(!this.rootVisible){
31187             node.ui = new Roo.tree.RootTreeNodeUI(node);
31188         }
31189         return node;
31190     },
31191
31192     /**
31193      * Returns the container element for this TreePanel
31194      */
31195     getEl : function(){
31196         return this.el;
31197     },
31198
31199     /**
31200      * Returns the default TreeLoader for this TreePanel
31201      */
31202     getLoader : function(){
31203         return this.loader;
31204     },
31205
31206     /**
31207      * Expand all nodes
31208      */
31209     expandAll : function(){
31210         this.root.expand(true);
31211     },
31212
31213     /**
31214      * Collapse all nodes
31215      */
31216     collapseAll : function(){
31217         this.root.collapse(true);
31218     },
31219
31220     /**
31221      * Returns the selection model used by this TreePanel
31222      */
31223     getSelectionModel : function(){
31224         if(!this.selModel){
31225             this.selModel = new Roo.tree.DefaultSelectionModel();
31226         }
31227         return this.selModel;
31228     },
31229
31230     /**
31231      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31232      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31233      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31234      * @return {Array}
31235      */
31236     getChecked : function(a, startNode){
31237         startNode = startNode || this.root;
31238         var r = [];
31239         var f = function(){
31240             if(this.attributes.checked){
31241                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31242             }
31243         }
31244         startNode.cascade(f);
31245         return r;
31246     },
31247
31248     /**
31249      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31250      * @param {String} path
31251      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31252      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31253      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31254      */
31255     expandPath : function(path, attr, callback){
31256         attr = attr || "id";
31257         var keys = path.split(this.pathSeparator);
31258         var curNode = this.root;
31259         if(curNode.attributes[attr] != keys[1]){ // invalid root
31260             if(callback){
31261                 callback(false, null);
31262             }
31263             return;
31264         }
31265         var index = 1;
31266         var f = function(){
31267             if(++index == keys.length){
31268                 if(callback){
31269                     callback(true, curNode);
31270                 }
31271                 return;
31272             }
31273             var c = curNode.findChild(attr, keys[index]);
31274             if(!c){
31275                 if(callback){
31276                     callback(false, curNode);
31277                 }
31278                 return;
31279             }
31280             curNode = c;
31281             c.expand(false, false, f);
31282         };
31283         curNode.expand(false, false, f);
31284     },
31285
31286     /**
31287      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31288      * @param {String} path
31289      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31290      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31291      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31292      */
31293     selectPath : function(path, attr, callback){
31294         attr = attr || "id";
31295         var keys = path.split(this.pathSeparator);
31296         var v = keys.pop();
31297         if(keys.length > 0){
31298             var f = function(success, node){
31299                 if(success && node){
31300                     var n = node.findChild(attr, v);
31301                     if(n){
31302                         n.select();
31303                         if(callback){
31304                             callback(true, n);
31305                         }
31306                     }else if(callback){
31307                         callback(false, n);
31308                     }
31309                 }else{
31310                     if(callback){
31311                         callback(false, n);
31312                     }
31313                 }
31314             };
31315             this.expandPath(keys.join(this.pathSeparator), attr, f);
31316         }else{
31317             this.root.select();
31318             if(callback){
31319                 callback(true, this.root);
31320             }
31321         }
31322     },
31323
31324     getTreeEl : function(){
31325         return this.el;
31326     },
31327
31328     /**
31329      * Trigger rendering of this TreePanel
31330      */
31331     render : function(){
31332         if (this.innerCt) {
31333             return this; // stop it rendering more than once!!
31334         }
31335         
31336         this.innerCt = this.el.createChild({tag:"ul",
31337                cls:"x-tree-root-ct " +
31338                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31339
31340         if(this.containerScroll){
31341             Roo.dd.ScrollManager.register(this.el);
31342         }
31343         if((this.enableDD || this.enableDrop) && !this.dropZone){
31344            /**
31345             * The dropZone used by this tree if drop is enabled
31346             * @type Roo.tree.TreeDropZone
31347             */
31348              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31349                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31350            });
31351         }
31352         if((this.enableDD || this.enableDrag) && !this.dragZone){
31353            /**
31354             * The dragZone used by this tree if drag is enabled
31355             * @type Roo.tree.TreeDragZone
31356             */
31357             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31358                ddGroup: this.ddGroup || "TreeDD",
31359                scroll: this.ddScroll
31360            });
31361         }
31362         this.getSelectionModel().init(this);
31363         if (!this.root) {
31364             console.log("ROOT not set in tree");
31365             return;
31366         }
31367         this.root.render();
31368         if(!this.rootVisible){
31369             this.root.renderChildren();
31370         }
31371         return this;
31372     }
31373 });/*
31374  * Based on:
31375  * Ext JS Library 1.1.1
31376  * Copyright(c) 2006-2007, Ext JS, LLC.
31377  *
31378  * Originally Released Under LGPL - original licence link has changed is not relivant.
31379  *
31380  * Fork - LGPL
31381  * <script type="text/javascript">
31382  */
31383  
31384
31385 /**
31386  * @class Roo.tree.DefaultSelectionModel
31387  * @extends Roo.util.Observable
31388  * The default single selection for a TreePanel.
31389  * @param {Object} cfg Configuration
31390  */
31391 Roo.tree.DefaultSelectionModel = function(cfg){
31392    this.selNode = null;
31393    
31394    
31395    
31396    this.addEvents({
31397        /**
31398         * @event selectionchange
31399         * Fires when the selected node changes
31400         * @param {DefaultSelectionModel} this
31401         * @param {TreeNode} node the new selection
31402         */
31403        "selectionchange" : true,
31404
31405        /**
31406         * @event beforeselect
31407         * Fires before the selected node changes, return false to cancel the change
31408         * @param {DefaultSelectionModel} this
31409         * @param {TreeNode} node the new selection
31410         * @param {TreeNode} node the old selection
31411         */
31412        "beforeselect" : true
31413    });
31414    
31415     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31416 };
31417
31418 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31419     init : function(tree){
31420         this.tree = tree;
31421         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31422         tree.on("click", this.onNodeClick, this);
31423     },
31424     
31425     onNodeClick : function(node, e){
31426         if (e.ctrlKey && this.selNode == node)  {
31427             this.unselect(node);
31428             return;
31429         }
31430         this.select(node);
31431     },
31432     
31433     /**
31434      * Select a node.
31435      * @param {TreeNode} node The node to select
31436      * @return {TreeNode} The selected node
31437      */
31438     select : function(node){
31439         var last = this.selNode;
31440         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31441             if(last){
31442                 last.ui.onSelectedChange(false);
31443             }
31444             this.selNode = node;
31445             node.ui.onSelectedChange(true);
31446             this.fireEvent("selectionchange", this, node, last);
31447         }
31448         return node;
31449     },
31450     
31451     /**
31452      * Deselect a node.
31453      * @param {TreeNode} node The node to unselect
31454      */
31455     unselect : function(node){
31456         if(this.selNode == node){
31457             this.clearSelections();
31458         }    
31459     },
31460     
31461     /**
31462      * Clear all selections
31463      */
31464     clearSelections : function(){
31465         var n = this.selNode;
31466         if(n){
31467             n.ui.onSelectedChange(false);
31468             this.selNode = null;
31469             this.fireEvent("selectionchange", this, null);
31470         }
31471         return n;
31472     },
31473     
31474     /**
31475      * Get the selected node
31476      * @return {TreeNode} The selected node
31477      */
31478     getSelectedNode : function(){
31479         return this.selNode;    
31480     },
31481     
31482     /**
31483      * Returns true if the node is selected
31484      * @param {TreeNode} node The node to check
31485      * @return {Boolean}
31486      */
31487     isSelected : function(node){
31488         return this.selNode == node;  
31489     },
31490
31491     /**
31492      * Selects the node above the selected node in the tree, intelligently walking the nodes
31493      * @return TreeNode The new selection
31494      */
31495     selectPrevious : function(){
31496         var s = this.selNode || this.lastSelNode;
31497         if(!s){
31498             return null;
31499         }
31500         var ps = s.previousSibling;
31501         if(ps){
31502             if(!ps.isExpanded() || ps.childNodes.length < 1){
31503                 return this.select(ps);
31504             } else{
31505                 var lc = ps.lastChild;
31506                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31507                     lc = lc.lastChild;
31508                 }
31509                 return this.select(lc);
31510             }
31511         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31512             return this.select(s.parentNode);
31513         }
31514         return null;
31515     },
31516
31517     /**
31518      * Selects the node above the selected node in the tree, intelligently walking the nodes
31519      * @return TreeNode The new selection
31520      */
31521     selectNext : function(){
31522         var s = this.selNode || this.lastSelNode;
31523         if(!s){
31524             return null;
31525         }
31526         if(s.firstChild && s.isExpanded()){
31527              return this.select(s.firstChild);
31528          }else if(s.nextSibling){
31529              return this.select(s.nextSibling);
31530          }else if(s.parentNode){
31531             var newS = null;
31532             s.parentNode.bubble(function(){
31533                 if(this.nextSibling){
31534                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31535                     return false;
31536                 }
31537             });
31538             return newS;
31539          }
31540         return null;
31541     },
31542
31543     onKeyDown : function(e){
31544         var s = this.selNode || this.lastSelNode;
31545         // undesirable, but required
31546         var sm = this;
31547         if(!s){
31548             return;
31549         }
31550         var k = e.getKey();
31551         switch(k){
31552              case e.DOWN:
31553                  e.stopEvent();
31554                  this.selectNext();
31555              break;
31556              case e.UP:
31557                  e.stopEvent();
31558                  this.selectPrevious();
31559              break;
31560              case e.RIGHT:
31561                  e.preventDefault();
31562                  if(s.hasChildNodes()){
31563                      if(!s.isExpanded()){
31564                          s.expand();
31565                      }else if(s.firstChild){
31566                          this.select(s.firstChild, e);
31567                      }
31568                  }
31569              break;
31570              case e.LEFT:
31571                  e.preventDefault();
31572                  if(s.hasChildNodes() && s.isExpanded()){
31573                      s.collapse();
31574                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31575                      this.select(s.parentNode, e);
31576                  }
31577              break;
31578         };
31579     }
31580 });
31581
31582 /**
31583  * @class Roo.tree.MultiSelectionModel
31584  * @extends Roo.util.Observable
31585  * Multi selection for a TreePanel.
31586  * @param {Object} cfg Configuration
31587  */
31588 Roo.tree.MultiSelectionModel = function(){
31589    this.selNodes = [];
31590    this.selMap = {};
31591    this.addEvents({
31592        /**
31593         * @event selectionchange
31594         * Fires when the selected nodes change
31595         * @param {MultiSelectionModel} this
31596         * @param {Array} nodes Array of the selected nodes
31597         */
31598        "selectionchange" : true
31599    });
31600    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
31601    
31602 };
31603
31604 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31605     init : function(tree){
31606         this.tree = tree;
31607         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31608         tree.on("click", this.onNodeClick, this);
31609     },
31610     
31611     onNodeClick : function(node, e){
31612         this.select(node, e, e.ctrlKey);
31613     },
31614     
31615     /**
31616      * Select a node.
31617      * @param {TreeNode} node The node to select
31618      * @param {EventObject} e (optional) An event associated with the selection
31619      * @param {Boolean} keepExisting True to retain existing selections
31620      * @return {TreeNode} The selected node
31621      */
31622     select : function(node, e, keepExisting){
31623         if(keepExisting !== true){
31624             this.clearSelections(true);
31625         }
31626         if(this.isSelected(node)){
31627             this.lastSelNode = node;
31628             return node;
31629         }
31630         this.selNodes.push(node);
31631         this.selMap[node.id] = node;
31632         this.lastSelNode = node;
31633         node.ui.onSelectedChange(true);
31634         this.fireEvent("selectionchange", this, this.selNodes);
31635         return node;
31636     },
31637     
31638     /**
31639      * Deselect a node.
31640      * @param {TreeNode} node The node to unselect
31641      */
31642     unselect : function(node){
31643         if(this.selMap[node.id]){
31644             node.ui.onSelectedChange(false);
31645             var sn = this.selNodes;
31646             var index = -1;
31647             if(sn.indexOf){
31648                 index = sn.indexOf(node);
31649             }else{
31650                 for(var i = 0, len = sn.length; i < len; i++){
31651                     if(sn[i] == node){
31652                         index = i;
31653                         break;
31654                     }
31655                 }
31656             }
31657             if(index != -1){
31658                 this.selNodes.splice(index, 1);
31659             }
31660             delete this.selMap[node.id];
31661             this.fireEvent("selectionchange", this, this.selNodes);
31662         }
31663     },
31664     
31665     /**
31666      * Clear all selections
31667      */
31668     clearSelections : function(suppressEvent){
31669         var sn = this.selNodes;
31670         if(sn.length > 0){
31671             for(var i = 0, len = sn.length; i < len; i++){
31672                 sn[i].ui.onSelectedChange(false);
31673             }
31674             this.selNodes = [];
31675             this.selMap = {};
31676             if(suppressEvent !== true){
31677                 this.fireEvent("selectionchange", this, this.selNodes);
31678             }
31679         }
31680     },
31681     
31682     /**
31683      * Returns true if the node is selected
31684      * @param {TreeNode} node The node to check
31685      * @return {Boolean}
31686      */
31687     isSelected : function(node){
31688         return this.selMap[node.id] ? true : false;  
31689     },
31690     
31691     /**
31692      * Returns an array of the selected nodes
31693      * @return {Array}
31694      */
31695     getSelectedNodes : function(){
31696         return this.selNodes;    
31697     },
31698
31699     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31700
31701     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31702
31703     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31704 });/*
31705  * Based on:
31706  * Ext JS Library 1.1.1
31707  * Copyright(c) 2006-2007, Ext JS, LLC.
31708  *
31709  * Originally Released Under LGPL - original licence link has changed is not relivant.
31710  *
31711  * Fork - LGPL
31712  * <script type="text/javascript">
31713  */
31714  
31715 /**
31716  * @class Roo.tree.TreeNode
31717  * @extends Roo.data.Node
31718  * @cfg {String} text The text for this node
31719  * @cfg {Boolean} expanded true to start the node expanded
31720  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31721  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31722  * @cfg {Boolean} disabled true to start the node disabled
31723  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31724  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31725  * @cfg {String} cls A css class to be added to the node
31726  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31727  * @cfg {String} href URL of the link used for the node (defaults to #)
31728  * @cfg {String} hrefTarget target frame for the link
31729  * @cfg {String} qtip An Ext QuickTip for the node
31730  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31731  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31732  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31733  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31734  * (defaults to undefined with no checkbox rendered)
31735  * @constructor
31736  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31737  */
31738 Roo.tree.TreeNode = function(attributes){
31739     attributes = attributes || {};
31740     if(typeof attributes == "string"){
31741         attributes = {text: attributes};
31742     }
31743     this.childrenRendered = false;
31744     this.rendered = false;
31745     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31746     this.expanded = attributes.expanded === true;
31747     this.isTarget = attributes.isTarget !== false;
31748     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31749     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31750
31751     /**
31752      * Read-only. The text for this node. To change it use setText().
31753      * @type String
31754      */
31755     this.text = attributes.text;
31756     /**
31757      * True if this node is disabled.
31758      * @type Boolean
31759      */
31760     this.disabled = attributes.disabled === true;
31761
31762     this.addEvents({
31763         /**
31764         * @event textchange
31765         * Fires when the text for this node is changed
31766         * @param {Node} this This node
31767         * @param {String} text The new text
31768         * @param {String} oldText The old text
31769         */
31770         "textchange" : true,
31771         /**
31772         * @event beforeexpand
31773         * Fires before this node is expanded, return false to cancel.
31774         * @param {Node} this This node
31775         * @param {Boolean} deep
31776         * @param {Boolean} anim
31777         */
31778         "beforeexpand" : true,
31779         /**
31780         * @event beforecollapse
31781         * Fires before this node is collapsed, return false to cancel.
31782         * @param {Node} this This node
31783         * @param {Boolean} deep
31784         * @param {Boolean} anim
31785         */
31786         "beforecollapse" : true,
31787         /**
31788         * @event expand
31789         * Fires when this node is expanded
31790         * @param {Node} this This node
31791         */
31792         "expand" : true,
31793         /**
31794         * @event disabledchange
31795         * Fires when the disabled status of this node changes
31796         * @param {Node} this This node
31797         * @param {Boolean} disabled
31798         */
31799         "disabledchange" : true,
31800         /**
31801         * @event collapse
31802         * Fires when this node is collapsed
31803         * @param {Node} this This node
31804         */
31805         "collapse" : true,
31806         /**
31807         * @event beforeclick
31808         * Fires before click processing. Return false to cancel the default action.
31809         * @param {Node} this This node
31810         * @param {Roo.EventObject} e The event object
31811         */
31812         "beforeclick":true,
31813         /**
31814         * @event checkchange
31815         * Fires when a node with a checkbox's checked property changes
31816         * @param {Node} this This node
31817         * @param {Boolean} checked
31818         */
31819         "checkchange":true,
31820         /**
31821         * @event click
31822         * Fires when this node is clicked
31823         * @param {Node} this This node
31824         * @param {Roo.EventObject} e The event object
31825         */
31826         "click":true,
31827         /**
31828         * @event dblclick
31829         * Fires when this node is double clicked
31830         * @param {Node} this This node
31831         * @param {Roo.EventObject} e The event object
31832         */
31833         "dblclick":true,
31834         /**
31835         * @event contextmenu
31836         * Fires when this node is right clicked
31837         * @param {Node} this This node
31838         * @param {Roo.EventObject} e The event object
31839         */
31840         "contextmenu":true,
31841         /**
31842         * @event beforechildrenrendered
31843         * Fires right before the child nodes for this node are rendered
31844         * @param {Node} this This node
31845         */
31846         "beforechildrenrendered":true
31847     });
31848
31849     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31850
31851     /**
31852      * Read-only. The UI for this node
31853      * @type TreeNodeUI
31854      */
31855     this.ui = new uiClass(this);
31856 };
31857 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31858     preventHScroll: true,
31859     /**
31860      * Returns true if this node is expanded
31861      * @return {Boolean}
31862      */
31863     isExpanded : function(){
31864         return this.expanded;
31865     },
31866
31867     /**
31868      * Returns the UI object for this node
31869      * @return {TreeNodeUI}
31870      */
31871     getUI : function(){
31872         return this.ui;
31873     },
31874
31875     // private override
31876     setFirstChild : function(node){
31877         var of = this.firstChild;
31878         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31879         if(this.childrenRendered && of && node != of){
31880             of.renderIndent(true, true);
31881         }
31882         if(this.rendered){
31883             this.renderIndent(true, true);
31884         }
31885     },
31886
31887     // private override
31888     setLastChild : function(node){
31889         var ol = this.lastChild;
31890         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31891         if(this.childrenRendered && ol && node != ol){
31892             ol.renderIndent(true, true);
31893         }
31894         if(this.rendered){
31895             this.renderIndent(true, true);
31896         }
31897     },
31898
31899     // these methods are overridden to provide lazy rendering support
31900     // private override
31901     appendChild : function(){
31902         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31903         if(node && this.childrenRendered){
31904             node.render();
31905         }
31906         this.ui.updateExpandIcon();
31907         return node;
31908     },
31909
31910     // private override
31911     removeChild : function(node){
31912         this.ownerTree.getSelectionModel().unselect(node);
31913         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31914         // if it's been rendered remove dom node
31915         if(this.childrenRendered){
31916             node.ui.remove();
31917         }
31918         if(this.childNodes.length < 1){
31919             this.collapse(false, false);
31920         }else{
31921             this.ui.updateExpandIcon();
31922         }
31923         if(!this.firstChild) {
31924             this.childrenRendered = false;
31925         }
31926         return node;
31927     },
31928
31929     // private override
31930     insertBefore : function(node, refNode){
31931         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31932         if(newNode && refNode && this.childrenRendered){
31933             node.render();
31934         }
31935         this.ui.updateExpandIcon();
31936         return newNode;
31937     },
31938
31939     /**
31940      * Sets the text for this node
31941      * @param {String} text
31942      */
31943     setText : function(text){
31944         var oldText = this.text;
31945         this.text = text;
31946         this.attributes.text = text;
31947         if(this.rendered){ // event without subscribing
31948             this.ui.onTextChange(this, text, oldText);
31949         }
31950         this.fireEvent("textchange", this, text, oldText);
31951     },
31952
31953     /**
31954      * Triggers selection of this node
31955      */
31956     select : function(){
31957         this.getOwnerTree().getSelectionModel().select(this);
31958     },
31959
31960     /**
31961      * Triggers deselection of this node
31962      */
31963     unselect : function(){
31964         this.getOwnerTree().getSelectionModel().unselect(this);
31965     },
31966
31967     /**
31968      * Returns true if this node is selected
31969      * @return {Boolean}
31970      */
31971     isSelected : function(){
31972         return this.getOwnerTree().getSelectionModel().isSelected(this);
31973     },
31974
31975     /**
31976      * Expand this node.
31977      * @param {Boolean} deep (optional) True to expand all children as well
31978      * @param {Boolean} anim (optional) false to cancel the default animation
31979      * @param {Function} callback (optional) A callback to be called when
31980      * expanding this node completes (does not wait for deep expand to complete).
31981      * Called with 1 parameter, this node.
31982      */
31983     expand : function(deep, anim, callback){
31984         if(!this.expanded){
31985             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31986                 return;
31987             }
31988             if(!this.childrenRendered){
31989                 this.renderChildren();
31990             }
31991             this.expanded = true;
31992             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31993                 this.ui.animExpand(function(){
31994                     this.fireEvent("expand", this);
31995                     if(typeof callback == "function"){
31996                         callback(this);
31997                     }
31998                     if(deep === true){
31999                         this.expandChildNodes(true);
32000                     }
32001                 }.createDelegate(this));
32002                 return;
32003             }else{
32004                 this.ui.expand();
32005                 this.fireEvent("expand", this);
32006                 if(typeof callback == "function"){
32007                     callback(this);
32008                 }
32009             }
32010         }else{
32011            if(typeof callback == "function"){
32012                callback(this);
32013            }
32014         }
32015         if(deep === true){
32016             this.expandChildNodes(true);
32017         }
32018     },
32019
32020     isHiddenRoot : function(){
32021         return this.isRoot && !this.getOwnerTree().rootVisible;
32022     },
32023
32024     /**
32025      * Collapse this node.
32026      * @param {Boolean} deep (optional) True to collapse all children as well
32027      * @param {Boolean} anim (optional) false to cancel the default animation
32028      */
32029     collapse : function(deep, anim){
32030         if(this.expanded && !this.isHiddenRoot()){
32031             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32032                 return;
32033             }
32034             this.expanded = false;
32035             if((this.getOwnerTree().animate && anim !== false) || anim){
32036                 this.ui.animCollapse(function(){
32037                     this.fireEvent("collapse", this);
32038                     if(deep === true){
32039                         this.collapseChildNodes(true);
32040                     }
32041                 }.createDelegate(this));
32042                 return;
32043             }else{
32044                 this.ui.collapse();
32045                 this.fireEvent("collapse", this);
32046             }
32047         }
32048         if(deep === true){
32049             var cs = this.childNodes;
32050             for(var i = 0, len = cs.length; i < len; i++) {
32051                 cs[i].collapse(true, false);
32052             }
32053         }
32054     },
32055
32056     // private
32057     delayedExpand : function(delay){
32058         if(!this.expandProcId){
32059             this.expandProcId = this.expand.defer(delay, this);
32060         }
32061     },
32062
32063     // private
32064     cancelExpand : function(){
32065         if(this.expandProcId){
32066             clearTimeout(this.expandProcId);
32067         }
32068         this.expandProcId = false;
32069     },
32070
32071     /**
32072      * Toggles expanded/collapsed state of the node
32073      */
32074     toggle : function(){
32075         if(this.expanded){
32076             this.collapse();
32077         }else{
32078             this.expand();
32079         }
32080     },
32081
32082     /**
32083      * Ensures all parent nodes are expanded
32084      */
32085     ensureVisible : function(callback){
32086         var tree = this.getOwnerTree();
32087         tree.expandPath(this.parentNode.getPath(), false, function(){
32088             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32089             Roo.callback(callback);
32090         }.createDelegate(this));
32091     },
32092
32093     /**
32094      * Expand all child nodes
32095      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32096      */
32097     expandChildNodes : function(deep){
32098         var cs = this.childNodes;
32099         for(var i = 0, len = cs.length; i < len; i++) {
32100                 cs[i].expand(deep);
32101         }
32102     },
32103
32104     /**
32105      * Collapse all child nodes
32106      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32107      */
32108     collapseChildNodes : function(deep){
32109         var cs = this.childNodes;
32110         for(var i = 0, len = cs.length; i < len; i++) {
32111                 cs[i].collapse(deep);
32112         }
32113     },
32114
32115     /**
32116      * Disables this node
32117      */
32118     disable : function(){
32119         this.disabled = true;
32120         this.unselect();
32121         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32122             this.ui.onDisableChange(this, true);
32123         }
32124         this.fireEvent("disabledchange", this, true);
32125     },
32126
32127     /**
32128      * Enables this node
32129      */
32130     enable : function(){
32131         this.disabled = false;
32132         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32133             this.ui.onDisableChange(this, false);
32134         }
32135         this.fireEvent("disabledchange", this, false);
32136     },
32137
32138     // private
32139     renderChildren : function(suppressEvent){
32140         if(suppressEvent !== false){
32141             this.fireEvent("beforechildrenrendered", this);
32142         }
32143         var cs = this.childNodes;
32144         for(var i = 0, len = cs.length; i < len; i++){
32145             cs[i].render(true);
32146         }
32147         this.childrenRendered = true;
32148     },
32149
32150     // private
32151     sort : function(fn, scope){
32152         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32153         if(this.childrenRendered){
32154             var cs = this.childNodes;
32155             for(var i = 0, len = cs.length; i < len; i++){
32156                 cs[i].render(true);
32157             }
32158         }
32159     },
32160
32161     // private
32162     render : function(bulkRender){
32163         this.ui.render(bulkRender);
32164         if(!this.rendered){
32165             this.rendered = true;
32166             if(this.expanded){
32167                 this.expanded = false;
32168                 this.expand(false, false);
32169             }
32170         }
32171     },
32172
32173     // private
32174     renderIndent : function(deep, refresh){
32175         if(refresh){
32176             this.ui.childIndent = null;
32177         }
32178         this.ui.renderIndent();
32179         if(deep === true && this.childrenRendered){
32180             var cs = this.childNodes;
32181             for(var i = 0, len = cs.length; i < len; i++){
32182                 cs[i].renderIndent(true, refresh);
32183             }
32184         }
32185     }
32186 });/*
32187  * Based on:
32188  * Ext JS Library 1.1.1
32189  * Copyright(c) 2006-2007, Ext JS, LLC.
32190  *
32191  * Originally Released Under LGPL - original licence link has changed is not relivant.
32192  *
32193  * Fork - LGPL
32194  * <script type="text/javascript">
32195  */
32196  
32197 /**
32198  * @class Roo.tree.AsyncTreeNode
32199  * @extends Roo.tree.TreeNode
32200  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32201  * @constructor
32202  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32203  */
32204  Roo.tree.AsyncTreeNode = function(config){
32205     this.loaded = false;
32206     this.loading = false;
32207     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32208     /**
32209     * @event beforeload
32210     * Fires before this node is loaded, return false to cancel
32211     * @param {Node} this This node
32212     */
32213     this.addEvents({'beforeload':true, 'load': true});
32214     /**
32215     * @event load
32216     * Fires when this node is loaded
32217     * @param {Node} this This node
32218     */
32219     /**
32220      * The loader used by this node (defaults to using the tree's defined loader)
32221      * @type TreeLoader
32222      * @property loader
32223      */
32224 };
32225 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32226     expand : function(deep, anim, callback){
32227         if(this.loading){ // if an async load is already running, waiting til it's done
32228             var timer;
32229             var f = function(){
32230                 if(!this.loading){ // done loading
32231                     clearInterval(timer);
32232                     this.expand(deep, anim, callback);
32233                 }
32234             }.createDelegate(this);
32235             timer = setInterval(f, 200);
32236             return;
32237         }
32238         if(!this.loaded){
32239             if(this.fireEvent("beforeload", this) === false){
32240                 return;
32241             }
32242             this.loading = true;
32243             this.ui.beforeLoad(this);
32244             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32245             if(loader){
32246                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32247                 return;
32248             }
32249         }
32250         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32251     },
32252     
32253     /**
32254      * Returns true if this node is currently loading
32255      * @return {Boolean}
32256      */
32257     isLoading : function(){
32258         return this.loading;  
32259     },
32260     
32261     loadComplete : function(deep, anim, callback){
32262         this.loading = false;
32263         this.loaded = true;
32264         this.ui.afterLoad(this);
32265         this.fireEvent("load", this);
32266         this.expand(deep, anim, callback);
32267     },
32268     
32269     /**
32270      * Returns true if this node has been loaded
32271      * @return {Boolean}
32272      */
32273     isLoaded : function(){
32274         return this.loaded;
32275     },
32276     
32277     hasChildNodes : function(){
32278         if(!this.isLeaf() && !this.loaded){
32279             return true;
32280         }else{
32281             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32282         }
32283     },
32284
32285     /**
32286      * Trigger a reload for this node
32287      * @param {Function} callback
32288      */
32289     reload : function(callback){
32290         this.collapse(false, false);
32291         while(this.firstChild){
32292             this.removeChild(this.firstChild);
32293         }
32294         this.childrenRendered = false;
32295         this.loaded = false;
32296         if(this.isHiddenRoot()){
32297             this.expanded = false;
32298         }
32299         this.expand(false, false, callback);
32300     }
32301 });/*
32302  * Based on:
32303  * Ext JS Library 1.1.1
32304  * Copyright(c) 2006-2007, Ext JS, LLC.
32305  *
32306  * Originally Released Under LGPL - original licence link has changed is not relivant.
32307  *
32308  * Fork - LGPL
32309  * <script type="text/javascript">
32310  */
32311  
32312 /**
32313  * @class Roo.tree.TreeNodeUI
32314  * @constructor
32315  * @param {Object} node The node to render
32316  * The TreeNode UI implementation is separate from the
32317  * tree implementation. Unless you are customizing the tree UI,
32318  * you should never have to use this directly.
32319  */
32320 Roo.tree.TreeNodeUI = function(node){
32321     this.node = node;
32322     this.rendered = false;
32323     this.animating = false;
32324     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32325 };
32326
32327 Roo.tree.TreeNodeUI.prototype = {
32328     removeChild : function(node){
32329         if(this.rendered){
32330             this.ctNode.removeChild(node.ui.getEl());
32331         }
32332     },
32333
32334     beforeLoad : function(){
32335          this.addClass("x-tree-node-loading");
32336     },
32337
32338     afterLoad : function(){
32339          this.removeClass("x-tree-node-loading");
32340     },
32341
32342     onTextChange : function(node, text, oldText){
32343         if(this.rendered){
32344             this.textNode.innerHTML = text;
32345         }
32346     },
32347
32348     onDisableChange : function(node, state){
32349         this.disabled = state;
32350         if(state){
32351             this.addClass("x-tree-node-disabled");
32352         }else{
32353             this.removeClass("x-tree-node-disabled");
32354         }
32355     },
32356
32357     onSelectedChange : function(state){
32358         if(state){
32359             this.focus();
32360             this.addClass("x-tree-selected");
32361         }else{
32362             //this.blur();
32363             this.removeClass("x-tree-selected");
32364         }
32365     },
32366
32367     onMove : function(tree, node, oldParent, newParent, index, refNode){
32368         this.childIndent = null;
32369         if(this.rendered){
32370             var targetNode = newParent.ui.getContainer();
32371             if(!targetNode){//target not rendered
32372                 this.holder = document.createElement("div");
32373                 this.holder.appendChild(this.wrap);
32374                 return;
32375             }
32376             var insertBefore = refNode ? refNode.ui.getEl() : null;
32377             if(insertBefore){
32378                 targetNode.insertBefore(this.wrap, insertBefore);
32379             }else{
32380                 targetNode.appendChild(this.wrap);
32381             }
32382             this.node.renderIndent(true);
32383         }
32384     },
32385
32386     addClass : function(cls){
32387         if(this.elNode){
32388             Roo.fly(this.elNode).addClass(cls);
32389         }
32390     },
32391
32392     removeClass : function(cls){
32393         if(this.elNode){
32394             Roo.fly(this.elNode).removeClass(cls);
32395         }
32396     },
32397
32398     remove : function(){
32399         if(this.rendered){
32400             this.holder = document.createElement("div");
32401             this.holder.appendChild(this.wrap);
32402         }
32403     },
32404
32405     fireEvent : function(){
32406         return this.node.fireEvent.apply(this.node, arguments);
32407     },
32408
32409     initEvents : function(){
32410         this.node.on("move", this.onMove, this);
32411         var E = Roo.EventManager;
32412         var a = this.anchor;
32413
32414         var el = Roo.fly(a, '_treeui');
32415
32416         if(Roo.isOpera){ // opera render bug ignores the CSS
32417             el.setStyle("text-decoration", "none");
32418         }
32419
32420         el.on("click", this.onClick, this);
32421         el.on("dblclick", this.onDblClick, this);
32422
32423         if(this.checkbox){
32424             Roo.EventManager.on(this.checkbox,
32425                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32426         }
32427
32428         el.on("contextmenu", this.onContextMenu, this);
32429
32430         var icon = Roo.fly(this.iconNode);
32431         icon.on("click", this.onClick, this);
32432         icon.on("dblclick", this.onDblClick, this);
32433         icon.on("contextmenu", this.onContextMenu, this);
32434         E.on(this.ecNode, "click", this.ecClick, this, true);
32435
32436         if(this.node.disabled){
32437             this.addClass("x-tree-node-disabled");
32438         }
32439         if(this.node.hidden){
32440             this.addClass("x-tree-node-disabled");
32441         }
32442         var ot = this.node.getOwnerTree();
32443         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32444         if(dd && (!this.node.isRoot || ot.rootVisible)){
32445             Roo.dd.Registry.register(this.elNode, {
32446                 node: this.node,
32447                 handles: this.getDDHandles(),
32448                 isHandle: false
32449             });
32450         }
32451     },
32452
32453     getDDHandles : function(){
32454         return [this.iconNode, this.textNode];
32455     },
32456
32457     hide : function(){
32458         if(this.rendered){
32459             this.wrap.style.display = "none";
32460         }
32461     },
32462
32463     show : function(){
32464         if(this.rendered){
32465             this.wrap.style.display = "";
32466         }
32467     },
32468
32469     onContextMenu : function(e){
32470         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32471             e.preventDefault();
32472             this.focus();
32473             this.fireEvent("contextmenu", this.node, e);
32474         }
32475     },
32476
32477     onClick : function(e){
32478         if(this.dropping){
32479             e.stopEvent();
32480             return;
32481         }
32482         if(this.fireEvent("beforeclick", this.node, e) !== false){
32483             if(!this.disabled && this.node.attributes.href){
32484                 this.fireEvent("click", this.node, e);
32485                 return;
32486             }
32487             e.preventDefault();
32488             if(this.disabled){
32489                 return;
32490             }
32491
32492             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32493                 this.node.toggle();
32494             }
32495
32496             this.fireEvent("click", this.node, e);
32497         }else{
32498             e.stopEvent();
32499         }
32500     },
32501
32502     onDblClick : function(e){
32503         e.preventDefault();
32504         if(this.disabled){
32505             return;
32506         }
32507         if(this.checkbox){
32508             this.toggleCheck();
32509         }
32510         if(!this.animating && this.node.hasChildNodes()){
32511             this.node.toggle();
32512         }
32513         this.fireEvent("dblclick", this.node, e);
32514     },
32515
32516     onCheckChange : function(){
32517         var checked = this.checkbox.checked;
32518         this.node.attributes.checked = checked;
32519         this.fireEvent('checkchange', this.node, checked);
32520     },
32521
32522     ecClick : function(e){
32523         if(!this.animating && this.node.hasChildNodes()){
32524             this.node.toggle();
32525         }
32526     },
32527
32528     startDrop : function(){
32529         this.dropping = true;
32530     },
32531
32532     // delayed drop so the click event doesn't get fired on a drop
32533     endDrop : function(){
32534        setTimeout(function(){
32535            this.dropping = false;
32536        }.createDelegate(this), 50);
32537     },
32538
32539     expand : function(){
32540         this.updateExpandIcon();
32541         this.ctNode.style.display = "";
32542     },
32543
32544     focus : function(){
32545         if(!this.node.preventHScroll){
32546             try{this.anchor.focus();
32547             }catch(e){}
32548         }else if(!Roo.isIE){
32549             try{
32550                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32551                 var l = noscroll.scrollLeft;
32552                 this.anchor.focus();
32553                 noscroll.scrollLeft = l;
32554             }catch(e){}
32555         }
32556     },
32557
32558     toggleCheck : function(value){
32559         var cb = this.checkbox;
32560         if(cb){
32561             cb.checked = (value === undefined ? !cb.checked : value);
32562         }
32563     },
32564
32565     blur : function(){
32566         try{
32567             this.anchor.blur();
32568         }catch(e){}
32569     },
32570
32571     animExpand : function(callback){
32572         var ct = Roo.get(this.ctNode);
32573         ct.stopFx();
32574         if(!this.node.hasChildNodes()){
32575             this.updateExpandIcon();
32576             this.ctNode.style.display = "";
32577             Roo.callback(callback);
32578             return;
32579         }
32580         this.animating = true;
32581         this.updateExpandIcon();
32582
32583         ct.slideIn('t', {
32584            callback : function(){
32585                this.animating = false;
32586                Roo.callback(callback);
32587             },
32588             scope: this,
32589             duration: this.node.ownerTree.duration || .25
32590         });
32591     },
32592
32593     highlight : function(){
32594         var tree = this.node.getOwnerTree();
32595         Roo.fly(this.wrap).highlight(
32596             tree.hlColor || "C3DAF9",
32597             {endColor: tree.hlBaseColor}
32598         );
32599     },
32600
32601     collapse : function(){
32602         this.updateExpandIcon();
32603         this.ctNode.style.display = "none";
32604     },
32605
32606     animCollapse : function(callback){
32607         var ct = Roo.get(this.ctNode);
32608         ct.enableDisplayMode('block');
32609         ct.stopFx();
32610
32611         this.animating = true;
32612         this.updateExpandIcon();
32613
32614         ct.slideOut('t', {
32615             callback : function(){
32616                this.animating = false;
32617                Roo.callback(callback);
32618             },
32619             scope: this,
32620             duration: this.node.ownerTree.duration || .25
32621         });
32622     },
32623
32624     getContainer : function(){
32625         return this.ctNode;
32626     },
32627
32628     getEl : function(){
32629         return this.wrap;
32630     },
32631
32632     appendDDGhost : function(ghostNode){
32633         ghostNode.appendChild(this.elNode.cloneNode(true));
32634     },
32635
32636     getDDRepairXY : function(){
32637         return Roo.lib.Dom.getXY(this.iconNode);
32638     },
32639
32640     onRender : function(){
32641         this.render();
32642     },
32643
32644     render : function(bulkRender){
32645         var n = this.node, a = n.attributes;
32646         var targetNode = n.parentNode ?
32647               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32648
32649         if(!this.rendered){
32650             this.rendered = true;
32651
32652             this.renderElements(n, a, targetNode, bulkRender);
32653
32654             if(a.qtip){
32655                if(this.textNode.setAttributeNS){
32656                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32657                    if(a.qtipTitle){
32658                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32659                    }
32660                }else{
32661                    this.textNode.setAttribute("ext:qtip", a.qtip);
32662                    if(a.qtipTitle){
32663                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32664                    }
32665                }
32666             }else if(a.qtipCfg){
32667                 a.qtipCfg.target = Roo.id(this.textNode);
32668                 Roo.QuickTips.register(a.qtipCfg);
32669             }
32670             this.initEvents();
32671             if(!this.node.expanded){
32672                 this.updateExpandIcon();
32673             }
32674         }else{
32675             if(bulkRender === true) {
32676                 targetNode.appendChild(this.wrap);
32677             }
32678         }
32679     },
32680
32681     renderElements : function(n, a, targetNode, bulkRender)
32682     {
32683         // add some indent caching, this helps performance when rendering a large tree
32684         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32685         var t = n.getOwnerTree();
32686         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32687         if (typeof(n.attributes.html) != 'undefined') {
32688             txt = n.attributes.html;
32689         }
32690         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32691         var cb = typeof a.checked == 'boolean';
32692         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32693         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32694             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32695             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32696             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32697             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32698             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32699              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32700                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32701             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32702             "</li>"];
32703
32704         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32705             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32706                                 n.nextSibling.ui.getEl(), buf.join(""));
32707         }else{
32708             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32709         }
32710
32711         this.elNode = this.wrap.childNodes[0];
32712         this.ctNode = this.wrap.childNodes[1];
32713         var cs = this.elNode.childNodes;
32714         this.indentNode = cs[0];
32715         this.ecNode = cs[1];
32716         this.iconNode = cs[2];
32717         var index = 3;
32718         if(cb){
32719             this.checkbox = cs[3];
32720             index++;
32721         }
32722         this.anchor = cs[index];
32723         this.textNode = cs[index].firstChild;
32724     },
32725
32726     getAnchor : function(){
32727         return this.anchor;
32728     },
32729
32730     getTextEl : function(){
32731         return this.textNode;
32732     },
32733
32734     getIconEl : function(){
32735         return this.iconNode;
32736     },
32737
32738     isChecked : function(){
32739         return this.checkbox ? this.checkbox.checked : false;
32740     },
32741
32742     updateExpandIcon : function(){
32743         if(this.rendered){
32744             var n = this.node, c1, c2;
32745             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32746             var hasChild = n.hasChildNodes();
32747             if(hasChild){
32748                 if(n.expanded){
32749                     cls += "-minus";
32750                     c1 = "x-tree-node-collapsed";
32751                     c2 = "x-tree-node-expanded";
32752                 }else{
32753                     cls += "-plus";
32754                     c1 = "x-tree-node-expanded";
32755                     c2 = "x-tree-node-collapsed";
32756                 }
32757                 if(this.wasLeaf){
32758                     this.removeClass("x-tree-node-leaf");
32759                     this.wasLeaf = false;
32760                 }
32761                 if(this.c1 != c1 || this.c2 != c2){
32762                     Roo.fly(this.elNode).replaceClass(c1, c2);
32763                     this.c1 = c1; this.c2 = c2;
32764                 }
32765             }else{
32766                 // this changes non-leafs into leafs if they have no children.
32767                 // it's not very rational behaviour..
32768                 
32769                 if(!this.wasLeaf && this.node.leaf){
32770                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32771                     delete this.c1;
32772                     delete this.c2;
32773                     this.wasLeaf = true;
32774                 }
32775             }
32776             var ecc = "x-tree-ec-icon "+cls;
32777             if(this.ecc != ecc){
32778                 this.ecNode.className = ecc;
32779                 this.ecc = ecc;
32780             }
32781         }
32782     },
32783
32784     getChildIndent : function(){
32785         if(!this.childIndent){
32786             var buf = [];
32787             var p = this.node;
32788             while(p){
32789                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32790                     if(!p.isLast()) {
32791                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32792                     } else {
32793                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32794                     }
32795                 }
32796                 p = p.parentNode;
32797             }
32798             this.childIndent = buf.join("");
32799         }
32800         return this.childIndent;
32801     },
32802
32803     renderIndent : function(){
32804         if(this.rendered){
32805             var indent = "";
32806             var p = this.node.parentNode;
32807             if(p){
32808                 indent = p.ui.getChildIndent();
32809             }
32810             if(this.indentMarkup != indent){ // don't rerender if not required
32811                 this.indentNode.innerHTML = indent;
32812                 this.indentMarkup = indent;
32813             }
32814             this.updateExpandIcon();
32815         }
32816     }
32817 };
32818
32819 Roo.tree.RootTreeNodeUI = function(){
32820     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32821 };
32822 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32823     render : function(){
32824         if(!this.rendered){
32825             var targetNode = this.node.ownerTree.innerCt.dom;
32826             this.node.expanded = true;
32827             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32828             this.wrap = this.ctNode = targetNode.firstChild;
32829         }
32830     },
32831     collapse : function(){
32832     },
32833     expand : function(){
32834     }
32835 });/*
32836  * Based on:
32837  * Ext JS Library 1.1.1
32838  * Copyright(c) 2006-2007, Ext JS, LLC.
32839  *
32840  * Originally Released Under LGPL - original licence link has changed is not relivant.
32841  *
32842  * Fork - LGPL
32843  * <script type="text/javascript">
32844  */
32845 /**
32846  * @class Roo.tree.TreeLoader
32847  * @extends Roo.util.Observable
32848  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32849  * nodes from a specified URL. The response must be a javascript Array definition
32850  * who's elements are node definition objects. eg:
32851  * <pre><code>
32852    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32853     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32854 </code></pre>
32855  * <br><br>
32856  * A server request is sent, and child nodes are loaded only when a node is expanded.
32857  * The loading node's id is passed to the server under the parameter name "node" to
32858  * enable the server to produce the correct child nodes.
32859  * <br><br>
32860  * To pass extra parameters, an event handler may be attached to the "beforeload"
32861  * event, and the parameters specified in the TreeLoader's baseParams property:
32862  * <pre><code>
32863     myTreeLoader.on("beforeload", function(treeLoader, node) {
32864         this.baseParams.category = node.attributes.category;
32865     }, this);
32866 </code></pre><
32867  * This would pass an HTTP parameter called "category" to the server containing
32868  * the value of the Node's "category" attribute.
32869  * @constructor
32870  * Creates a new Treeloader.
32871  * @param {Object} config A config object containing config properties.
32872  */
32873 Roo.tree.TreeLoader = function(config){
32874     this.baseParams = {};
32875     this.requestMethod = "POST";
32876     Roo.apply(this, config);
32877
32878     this.addEvents({
32879     
32880         /**
32881          * @event beforeload
32882          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32883          * @param {Object} This TreeLoader object.
32884          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32885          * @param {Object} callback The callback function specified in the {@link #load} call.
32886          */
32887         beforeload : true,
32888         /**
32889          * @event load
32890          * Fires when the node has been successfuly loaded.
32891          * @param {Object} This TreeLoader object.
32892          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32893          * @param {Object} response The response object containing the data from the server.
32894          */
32895         load : true,
32896         /**
32897          * @event loadexception
32898          * Fires if the network request failed.
32899          * @param {Object} This TreeLoader object.
32900          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32901          * @param {Object} response The response object containing the data from the server.
32902          */
32903         loadexception : true,
32904         /**
32905          * @event create
32906          * Fires before a node is created, enabling you to return custom Node types 
32907          * @param {Object} This TreeLoader object.
32908          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32909          */
32910         create : true
32911     });
32912
32913     Roo.tree.TreeLoader.superclass.constructor.call(this);
32914 };
32915
32916 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32917     /**
32918     * @cfg {String} dataUrl The URL from which to request a Json string which
32919     * specifies an array of node definition object representing the child nodes
32920     * to be loaded.
32921     */
32922     /**
32923     * @cfg {Object} baseParams (optional) An object containing properties which
32924     * specify HTTP parameters to be passed to each request for child nodes.
32925     */
32926     /**
32927     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32928     * created by this loader. If the attributes sent by the server have an attribute in this object,
32929     * they take priority.
32930     */
32931     /**
32932     * @cfg {Object} uiProviders (optional) An object containing properties which
32933     * 
32934     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
32935     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32936     * <i>uiProvider</i> attribute of a returned child node is a string rather
32937     * than a reference to a TreeNodeUI implementation, this that string value
32938     * is used as a property name in the uiProviders object. You can define the provider named
32939     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32940     */
32941     uiProviders : {},
32942
32943     /**
32944     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32945     * child nodes before loading.
32946     */
32947     clearOnLoad : true,
32948
32949     /**
32950     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32951     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32952     * Grid query { data : [ .....] }
32953     */
32954     
32955     root : false,
32956      /**
32957     * @cfg {String} queryParam (optional) 
32958     * Name of the query as it will be passed on the querystring (defaults to 'node')
32959     * eg. the request will be ?node=[id]
32960     */
32961     
32962     
32963     queryParam: false,
32964     
32965     /**
32966      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32967      * This is called automatically when a node is expanded, but may be used to reload
32968      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32969      * @param {Roo.tree.TreeNode} node
32970      * @param {Function} callback
32971      */
32972     load : function(node, callback){
32973         if(this.clearOnLoad){
32974             while(node.firstChild){
32975                 node.removeChild(node.firstChild);
32976             }
32977         }
32978         if(node.attributes.children){ // preloaded json children
32979             var cs = node.attributes.children;
32980             for(var i = 0, len = cs.length; i < len; i++){
32981                 node.appendChild(this.createNode(cs[i]));
32982             }
32983             if(typeof callback == "function"){
32984                 callback();
32985             }
32986         }else if(this.dataUrl){
32987             this.requestData(node, callback);
32988         }
32989     },
32990
32991     getParams: function(node){
32992         var buf = [], bp = this.baseParams;
32993         for(var key in bp){
32994             if(typeof bp[key] != "function"){
32995                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32996             }
32997         }
32998         var n = this.queryParam === false ? 'node' : this.queryParam;
32999         buf.push(n + "=", encodeURIComponent(node.id));
33000         return buf.join("");
33001     },
33002
33003     requestData : function(node, callback){
33004         if(this.fireEvent("beforeload", this, node, callback) !== false){
33005             this.transId = Roo.Ajax.request({
33006                 method:this.requestMethod,
33007                 url: this.dataUrl||this.url,
33008                 success: this.handleResponse,
33009                 failure: this.handleFailure,
33010                 scope: this,
33011                 argument: {callback: callback, node: node},
33012                 params: this.getParams(node)
33013             });
33014         }else{
33015             // if the load is cancelled, make sure we notify
33016             // the node that we are done
33017             if(typeof callback == "function"){
33018                 callback();
33019             }
33020         }
33021     },
33022
33023     isLoading : function(){
33024         return this.transId ? true : false;
33025     },
33026
33027     abort : function(){
33028         if(this.isLoading()){
33029             Roo.Ajax.abort(this.transId);
33030         }
33031     },
33032
33033     // private
33034     createNode : function(attr)
33035     {
33036         // apply baseAttrs, nice idea Corey!
33037         if(this.baseAttrs){
33038             Roo.applyIf(attr, this.baseAttrs);
33039         }
33040         if(this.applyLoader !== false){
33041             attr.loader = this;
33042         }
33043         // uiProvider = depreciated..
33044         
33045         if(typeof(attr.uiProvider) == 'string'){
33046            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33047                 /**  eval:var:attr */ eval(attr.uiProvider);
33048         }
33049         if(typeof(this.uiProviders['default']) != 'undefined') {
33050             attr.uiProvider = this.uiProviders['default'];
33051         }
33052         
33053         this.fireEvent('create', this, attr);
33054         
33055         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33056         return(attr.leaf ?
33057                         new Roo.tree.TreeNode(attr) :
33058                         new Roo.tree.AsyncTreeNode(attr));
33059     },
33060
33061     processResponse : function(response, node, callback)
33062     {
33063         var json = response.responseText;
33064         try {
33065             
33066             var o = Roo.decode(json);
33067             
33068             if (!o.success) {
33069                 // it's a failure condition.
33070                 var a = response.argument;
33071                 this.fireEvent("loadexception", this, a.node, response);
33072                 Roo.log("Load failed - should have a handler really");
33073                 return;
33074             }
33075             
33076             if (this.root !== false) {
33077                 o = o[this.root];
33078             }
33079             
33080             for(var i = 0, len = o.length; i < len; i++){
33081                 var n = this.createNode(o[i]);
33082                 if(n){
33083                     node.appendChild(n);
33084                 }
33085             }
33086             if(typeof callback == "function"){
33087                 callback(this, node);
33088             }
33089         }catch(e){
33090             this.handleFailure(response);
33091         }
33092     },
33093
33094     handleResponse : function(response){
33095         this.transId = false;
33096         var a = response.argument;
33097         this.processResponse(response, a.node, a.callback);
33098         this.fireEvent("load", this, a.node, response);
33099     },
33100
33101     handleFailure : function(response)
33102     {
33103         // should handle failure better..
33104         this.transId = false;
33105         var a = response.argument;
33106         this.fireEvent("loadexception", this, a.node, response);
33107         if(typeof a.callback == "function"){
33108             a.callback(this, a.node);
33109         }
33110     }
33111 });/*
33112  * Based on:
33113  * Ext JS Library 1.1.1
33114  * Copyright(c) 2006-2007, Ext JS, LLC.
33115  *
33116  * Originally Released Under LGPL - original licence link has changed is not relivant.
33117  *
33118  * Fork - LGPL
33119  * <script type="text/javascript">
33120  */
33121
33122 /**
33123 * @class Roo.tree.TreeFilter
33124 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33125 * @param {TreePanel} tree
33126 * @param {Object} config (optional)
33127  */
33128 Roo.tree.TreeFilter = function(tree, config){
33129     this.tree = tree;
33130     this.filtered = {};
33131     Roo.apply(this, config);
33132 };
33133
33134 Roo.tree.TreeFilter.prototype = {
33135     clearBlank:false,
33136     reverse:false,
33137     autoClear:false,
33138     remove:false,
33139
33140      /**
33141      * Filter the data by a specific attribute.
33142      * @param {String/RegExp} value Either string that the attribute value
33143      * should start with or a RegExp to test against the attribute
33144      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33145      * @param {TreeNode} startNode (optional) The node to start the filter at.
33146      */
33147     filter : function(value, attr, startNode){
33148         attr = attr || "text";
33149         var f;
33150         if(typeof value == "string"){
33151             var vlen = value.length;
33152             // auto clear empty filter
33153             if(vlen == 0 && this.clearBlank){
33154                 this.clear();
33155                 return;
33156             }
33157             value = value.toLowerCase();
33158             f = function(n){
33159                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33160             };
33161         }else if(value.exec){ // regex?
33162             f = function(n){
33163                 return value.test(n.attributes[attr]);
33164             };
33165         }else{
33166             throw 'Illegal filter type, must be string or regex';
33167         }
33168         this.filterBy(f, null, startNode);
33169         },
33170
33171     /**
33172      * Filter by a function. The passed function will be called with each
33173      * node in the tree (or from the startNode). If the function returns true, the node is kept
33174      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33175      * @param {Function} fn The filter function
33176      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33177      */
33178     filterBy : function(fn, scope, startNode){
33179         startNode = startNode || this.tree.root;
33180         if(this.autoClear){
33181             this.clear();
33182         }
33183         var af = this.filtered, rv = this.reverse;
33184         var f = function(n){
33185             if(n == startNode){
33186                 return true;
33187             }
33188             if(af[n.id]){
33189                 return false;
33190             }
33191             var m = fn.call(scope || n, n);
33192             if(!m || rv){
33193                 af[n.id] = n;
33194                 n.ui.hide();
33195                 return false;
33196             }
33197             return true;
33198         };
33199         startNode.cascade(f);
33200         if(this.remove){
33201            for(var id in af){
33202                if(typeof id != "function"){
33203                    var n = af[id];
33204                    if(n && n.parentNode){
33205                        n.parentNode.removeChild(n);
33206                    }
33207                }
33208            }
33209         }
33210     },
33211
33212     /**
33213      * Clears the current filter. Note: with the "remove" option
33214      * set a filter cannot be cleared.
33215      */
33216     clear : function(){
33217         var t = this.tree;
33218         var af = this.filtered;
33219         for(var id in af){
33220             if(typeof id != "function"){
33221                 var n = af[id];
33222                 if(n){
33223                     n.ui.show();
33224                 }
33225             }
33226         }
33227         this.filtered = {};
33228     }
33229 };
33230 /*
33231  * Based on:
33232  * Ext JS Library 1.1.1
33233  * Copyright(c) 2006-2007, Ext JS, LLC.
33234  *
33235  * Originally Released Under LGPL - original licence link has changed is not relivant.
33236  *
33237  * Fork - LGPL
33238  * <script type="text/javascript">
33239  */
33240  
33241
33242 /**
33243  * @class Roo.tree.TreeSorter
33244  * Provides sorting of nodes in a TreePanel
33245  * 
33246  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33247  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33248  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33249  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33250  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33251  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33252  * @constructor
33253  * @param {TreePanel} tree
33254  * @param {Object} config
33255  */
33256 Roo.tree.TreeSorter = function(tree, config){
33257     Roo.apply(this, config);
33258     tree.on("beforechildrenrendered", this.doSort, this);
33259     tree.on("append", this.updateSort, this);
33260     tree.on("insert", this.updateSort, this);
33261     
33262     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33263     var p = this.property || "text";
33264     var sortType = this.sortType;
33265     var fs = this.folderSort;
33266     var cs = this.caseSensitive === true;
33267     var leafAttr = this.leafAttr || 'leaf';
33268
33269     this.sortFn = function(n1, n2){
33270         if(fs){
33271             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33272                 return 1;
33273             }
33274             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33275                 return -1;
33276             }
33277         }
33278         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33279         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33280         if(v1 < v2){
33281                         return dsc ? +1 : -1;
33282                 }else if(v1 > v2){
33283                         return dsc ? -1 : +1;
33284         }else{
33285                 return 0;
33286         }
33287     };
33288 };
33289
33290 Roo.tree.TreeSorter.prototype = {
33291     doSort : function(node){
33292         node.sort(this.sortFn);
33293     },
33294     
33295     compareNodes : function(n1, n2){
33296         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33297     },
33298     
33299     updateSort : function(tree, node){
33300         if(node.childrenRendered){
33301             this.doSort.defer(1, this, [node]);
33302         }
33303     }
33304 };/*
33305  * Based on:
33306  * Ext JS Library 1.1.1
33307  * Copyright(c) 2006-2007, Ext JS, LLC.
33308  *
33309  * Originally Released Under LGPL - original licence link has changed is not relivant.
33310  *
33311  * Fork - LGPL
33312  * <script type="text/javascript">
33313  */
33314
33315 if(Roo.dd.DropZone){
33316     
33317 Roo.tree.TreeDropZone = function(tree, config){
33318     this.allowParentInsert = false;
33319     this.allowContainerDrop = false;
33320     this.appendOnly = false;
33321     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33322     this.tree = tree;
33323     this.lastInsertClass = "x-tree-no-status";
33324     this.dragOverData = {};
33325 };
33326
33327 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33328     ddGroup : "TreeDD",
33329     
33330     expandDelay : 1000,
33331     
33332     expandNode : function(node){
33333         if(node.hasChildNodes() && !node.isExpanded()){
33334             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33335         }
33336     },
33337     
33338     queueExpand : function(node){
33339         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33340     },
33341     
33342     cancelExpand : function(){
33343         if(this.expandProcId){
33344             clearTimeout(this.expandProcId);
33345             this.expandProcId = false;
33346         }
33347     },
33348     
33349     isValidDropPoint : function(n, pt, dd, e, data){
33350         if(!n || !data){ return false; }
33351         var targetNode = n.node;
33352         var dropNode = data.node;
33353         // default drop rules
33354         if(!(targetNode && targetNode.isTarget && pt)){
33355             return false;
33356         }
33357         if(pt == "append" && targetNode.allowChildren === false){
33358             return false;
33359         }
33360         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33361             return false;
33362         }
33363         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33364             return false;
33365         }
33366         // reuse the object
33367         var overEvent = this.dragOverData;
33368         overEvent.tree = this.tree;
33369         overEvent.target = targetNode;
33370         overEvent.data = data;
33371         overEvent.point = pt;
33372         overEvent.source = dd;
33373         overEvent.rawEvent = e;
33374         overEvent.dropNode = dropNode;
33375         overEvent.cancel = false;  
33376         var result = this.tree.fireEvent("nodedragover", overEvent);
33377         return overEvent.cancel === false && result !== false;
33378     },
33379     
33380     getDropPoint : function(e, n, dd){
33381         var tn = n.node;
33382         if(tn.isRoot){
33383             return tn.allowChildren !== false ? "append" : false; // always append for root
33384         }
33385         var dragEl = n.ddel;
33386         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33387         var y = Roo.lib.Event.getPageY(e);
33388         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33389         
33390         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33391         var noAppend = tn.allowChildren === false;
33392         if(this.appendOnly || tn.parentNode.allowChildren === false){
33393             return noAppend ? false : "append";
33394         }
33395         var noBelow = false;
33396         if(!this.allowParentInsert){
33397             noBelow = tn.hasChildNodes() && tn.isExpanded();
33398         }
33399         var q = (b - t) / (noAppend ? 2 : 3);
33400         if(y >= t && y < (t + q)){
33401             return "above";
33402         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33403             return "below";
33404         }else{
33405             return "append";
33406         }
33407     },
33408     
33409     onNodeEnter : function(n, dd, e, data){
33410         this.cancelExpand();
33411     },
33412     
33413     onNodeOver : function(n, dd, e, data){
33414         var pt = this.getDropPoint(e, n, dd);
33415         var node = n.node;
33416         
33417         // auto node expand check
33418         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33419             this.queueExpand(node);
33420         }else if(pt != "append"){
33421             this.cancelExpand();
33422         }
33423         
33424         // set the insert point style on the target node
33425         var returnCls = this.dropNotAllowed;
33426         if(this.isValidDropPoint(n, pt, dd, e, data)){
33427            if(pt){
33428                var el = n.ddel;
33429                var cls;
33430                if(pt == "above"){
33431                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33432                    cls = "x-tree-drag-insert-above";
33433                }else if(pt == "below"){
33434                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33435                    cls = "x-tree-drag-insert-below";
33436                }else{
33437                    returnCls = "x-tree-drop-ok-append";
33438                    cls = "x-tree-drag-append";
33439                }
33440                if(this.lastInsertClass != cls){
33441                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33442                    this.lastInsertClass = cls;
33443                }
33444            }
33445        }
33446        return returnCls;
33447     },
33448     
33449     onNodeOut : function(n, dd, e, data){
33450         this.cancelExpand();
33451         this.removeDropIndicators(n);
33452     },
33453     
33454     onNodeDrop : function(n, dd, e, data){
33455         var point = this.getDropPoint(e, n, dd);
33456         var targetNode = n.node;
33457         targetNode.ui.startDrop();
33458         if(!this.isValidDropPoint(n, point, dd, e, data)){
33459             targetNode.ui.endDrop();
33460             return false;
33461         }
33462         // first try to find the drop node
33463         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33464         var dropEvent = {
33465             tree : this.tree,
33466             target: targetNode,
33467             data: data,
33468             point: point,
33469             source: dd,
33470             rawEvent: e,
33471             dropNode: dropNode,
33472             cancel: !dropNode   
33473         };
33474         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33475         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33476             targetNode.ui.endDrop();
33477             return false;
33478         }
33479         // allow target changing
33480         targetNode = dropEvent.target;
33481         if(point == "append" && !targetNode.isExpanded()){
33482             targetNode.expand(false, null, function(){
33483                 this.completeDrop(dropEvent);
33484             }.createDelegate(this));
33485         }else{
33486             this.completeDrop(dropEvent);
33487         }
33488         return true;
33489     },
33490     
33491     completeDrop : function(de){
33492         var ns = de.dropNode, p = de.point, t = de.target;
33493         if(!(ns instanceof Array)){
33494             ns = [ns];
33495         }
33496         var n;
33497         for(var i = 0, len = ns.length; i < len; i++){
33498             n = ns[i];
33499             if(p == "above"){
33500                 t.parentNode.insertBefore(n, t);
33501             }else if(p == "below"){
33502                 t.parentNode.insertBefore(n, t.nextSibling);
33503             }else{
33504                 t.appendChild(n);
33505             }
33506         }
33507         n.ui.focus();
33508         if(this.tree.hlDrop){
33509             n.ui.highlight();
33510         }
33511         t.ui.endDrop();
33512         this.tree.fireEvent("nodedrop", de);
33513     },
33514     
33515     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33516         if(this.tree.hlDrop){
33517             dropNode.ui.focus();
33518             dropNode.ui.highlight();
33519         }
33520         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33521     },
33522     
33523     getTree : function(){
33524         return this.tree;
33525     },
33526     
33527     removeDropIndicators : function(n){
33528         if(n && n.ddel){
33529             var el = n.ddel;
33530             Roo.fly(el).removeClass([
33531                     "x-tree-drag-insert-above",
33532                     "x-tree-drag-insert-below",
33533                     "x-tree-drag-append"]);
33534             this.lastInsertClass = "_noclass";
33535         }
33536     },
33537     
33538     beforeDragDrop : function(target, e, id){
33539         this.cancelExpand();
33540         return true;
33541     },
33542     
33543     afterRepair : function(data){
33544         if(data && Roo.enableFx){
33545             data.node.ui.highlight();
33546         }
33547         this.hideProxy();
33548     }    
33549 });
33550
33551 }
33552 /*
33553  * Based on:
33554  * Ext JS Library 1.1.1
33555  * Copyright(c) 2006-2007, Ext JS, LLC.
33556  *
33557  * Originally Released Under LGPL - original licence link has changed is not relivant.
33558  *
33559  * Fork - LGPL
33560  * <script type="text/javascript">
33561  */
33562  
33563
33564 if(Roo.dd.DragZone){
33565 Roo.tree.TreeDragZone = function(tree, config){
33566     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33567     this.tree = tree;
33568 };
33569
33570 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33571     ddGroup : "TreeDD",
33572     
33573     onBeforeDrag : function(data, e){
33574         var n = data.node;
33575         return n && n.draggable && !n.disabled;
33576     },
33577     
33578     onInitDrag : function(e){
33579         var data = this.dragData;
33580         this.tree.getSelectionModel().select(data.node);
33581         this.proxy.update("");
33582         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33583         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33584     },
33585     
33586     getRepairXY : function(e, data){
33587         return data.node.ui.getDDRepairXY();
33588     },
33589     
33590     onEndDrag : function(data, e){
33591         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33592     },
33593     
33594     onValidDrop : function(dd, e, id){
33595         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33596         this.hideProxy();
33597     },
33598     
33599     beforeInvalidDrop : function(e, id){
33600         // this scrolls the original position back into view
33601         var sm = this.tree.getSelectionModel();
33602         sm.clearSelections();
33603         sm.select(this.dragData.node);
33604     }
33605 });
33606 }/*
33607  * Based on:
33608  * Ext JS Library 1.1.1
33609  * Copyright(c) 2006-2007, Ext JS, LLC.
33610  *
33611  * Originally Released Under LGPL - original licence link has changed is not relivant.
33612  *
33613  * Fork - LGPL
33614  * <script type="text/javascript">
33615  */
33616 /**
33617  * @class Roo.tree.TreeEditor
33618  * @extends Roo.Editor
33619  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33620  * as the editor field.
33621  * @constructor
33622  * @param {Object} config (used to be the tree panel.)
33623  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33624  * 
33625  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
33626  * @cfg {Roo.form.TextField|Object} field The field configuration
33627  *
33628  * 
33629  */
33630 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
33631     var tree = config;
33632     var field;
33633     if (oldconfig) { // old style..
33634         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
33635     } else {
33636         // new style..
33637         tree = config.tree;
33638         config.field = config.field  || {};
33639         config.field.xtype = 'TextField';
33640         field = Roo.factory(config.field, Roo.form);
33641     }
33642     config = config || {};
33643     
33644     
33645     this.addEvents({
33646         /**
33647          * @event beforenodeedit
33648          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33649          * false from the handler of this event.
33650          * @param {Editor} this
33651          * @param {Roo.tree.Node} node 
33652          */
33653         "beforenodeedit" : true
33654     });
33655     
33656     //Roo.log(config);
33657     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
33658
33659     this.tree = tree;
33660
33661     tree.on('beforeclick', this.beforeNodeClick, this);
33662     tree.getTreeEl().on('mousedown', this.hide, this);
33663     this.on('complete', this.updateNode, this);
33664     this.on('beforestartedit', this.fitToTree, this);
33665     this.on('startedit', this.bindScroll, this, {delay:10});
33666     this.on('specialkey', this.onSpecialKey, this);
33667 };
33668
33669 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33670     /**
33671      * @cfg {String} alignment
33672      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33673      */
33674     alignment: "l-l",
33675     // inherit
33676     autoSize: false,
33677     /**
33678      * @cfg {Boolean} hideEl
33679      * True to hide the bound element while the editor is displayed (defaults to false)
33680      */
33681     hideEl : false,
33682     /**
33683      * @cfg {String} cls
33684      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33685      */
33686     cls: "x-small-editor x-tree-editor",
33687     /**
33688      * @cfg {Boolean} shim
33689      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33690      */
33691     shim:false,
33692     // inherit
33693     shadow:"frame",
33694     /**
33695      * @cfg {Number} maxWidth
33696      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33697      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33698      * scroll and client offsets into account prior to each edit.
33699      */
33700     maxWidth: 250,
33701
33702     editDelay : 350,
33703
33704     // private
33705     fitToTree : function(ed, el){
33706         var td = this.tree.getTreeEl().dom, nd = el.dom;
33707         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33708             td.scrollLeft = nd.offsetLeft;
33709         }
33710         var w = Math.min(
33711                 this.maxWidth,
33712                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33713         this.setSize(w, '');
33714         
33715         return this.fireEvent('beforenodeedit', this, this.editNode);
33716         
33717     },
33718
33719     // private
33720     triggerEdit : function(node){
33721         this.completeEdit();
33722         this.editNode = node;
33723         this.startEdit(node.ui.textNode, node.text);
33724     },
33725
33726     // private
33727     bindScroll : function(){
33728         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33729     },
33730
33731     // private
33732     beforeNodeClick : function(node, e){
33733         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33734         this.lastClick = new Date();
33735         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33736             e.stopEvent();
33737             this.triggerEdit(node);
33738             return false;
33739         }
33740         return true;
33741     },
33742
33743     // private
33744     updateNode : function(ed, value){
33745         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33746         this.editNode.setText(value);
33747     },
33748
33749     // private
33750     onHide : function(){
33751         Roo.tree.TreeEditor.superclass.onHide.call(this);
33752         if(this.editNode){
33753             this.editNode.ui.focus();
33754         }
33755     },
33756
33757     // private
33758     onSpecialKey : function(field, e){
33759         var k = e.getKey();
33760         if(k == e.ESC){
33761             e.stopEvent();
33762             this.cancelEdit();
33763         }else if(k == e.ENTER && !e.hasModifier()){
33764             e.stopEvent();
33765             this.completeEdit();
33766         }
33767     }
33768 });//<Script type="text/javascript">
33769 /*
33770  * Based on:
33771  * Ext JS Library 1.1.1
33772  * Copyright(c) 2006-2007, Ext JS, LLC.
33773  *
33774  * Originally Released Under LGPL - original licence link has changed is not relivant.
33775  *
33776  * Fork - LGPL
33777  * <script type="text/javascript">
33778  */
33779  
33780 /**
33781  * Not documented??? - probably should be...
33782  */
33783
33784 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33785     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33786     
33787     renderElements : function(n, a, targetNode, bulkRender){
33788         //consel.log("renderElements?");
33789         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33790
33791         var t = n.getOwnerTree();
33792         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33793         
33794         var cols = t.columns;
33795         var bw = t.borderWidth;
33796         var c = cols[0];
33797         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33798          var cb = typeof a.checked == "boolean";
33799         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33800         var colcls = 'x-t-' + tid + '-c0';
33801         var buf = [
33802             '<li class="x-tree-node">',
33803             
33804                 
33805                 '<div class="x-tree-node-el ', a.cls,'">',
33806                     // extran...
33807                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33808                 
33809                 
33810                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33811                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33812                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33813                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33814                            (a.iconCls ? ' '+a.iconCls : ''),
33815                            '" unselectable="on" />',
33816                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33817                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33818                              
33819                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33820                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33821                             '<span unselectable="on" qtip="' + tx + '">',
33822                              tx,
33823                              '</span></a>' ,
33824                     '</div>',
33825                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33826                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33827                  ];
33828         for(var i = 1, len = cols.length; i < len; i++){
33829             c = cols[i];
33830             colcls = 'x-t-' + tid + '-c' +i;
33831             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33832             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33833                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33834                       "</div>");
33835          }
33836          
33837          buf.push(
33838             '</a>',
33839             '<div class="x-clear"></div></div>',
33840             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33841             "</li>");
33842         
33843         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33844             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33845                                 n.nextSibling.ui.getEl(), buf.join(""));
33846         }else{
33847             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33848         }
33849         var el = this.wrap.firstChild;
33850         this.elRow = el;
33851         this.elNode = el.firstChild;
33852         this.ranchor = el.childNodes[1];
33853         this.ctNode = this.wrap.childNodes[1];
33854         var cs = el.firstChild.childNodes;
33855         this.indentNode = cs[0];
33856         this.ecNode = cs[1];
33857         this.iconNode = cs[2];
33858         var index = 3;
33859         if(cb){
33860             this.checkbox = cs[3];
33861             index++;
33862         }
33863         this.anchor = cs[index];
33864         
33865         this.textNode = cs[index].firstChild;
33866         
33867         //el.on("click", this.onClick, this);
33868         //el.on("dblclick", this.onDblClick, this);
33869         
33870         
33871        // console.log(this);
33872     },
33873     initEvents : function(){
33874         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33875         
33876             
33877         var a = this.ranchor;
33878
33879         var el = Roo.get(a);
33880
33881         if(Roo.isOpera){ // opera render bug ignores the CSS
33882             el.setStyle("text-decoration", "none");
33883         }
33884
33885         el.on("click", this.onClick, this);
33886         el.on("dblclick", this.onDblClick, this);
33887         el.on("contextmenu", this.onContextMenu, this);
33888         
33889     },
33890     
33891     /*onSelectedChange : function(state){
33892         if(state){
33893             this.focus();
33894             this.addClass("x-tree-selected");
33895         }else{
33896             //this.blur();
33897             this.removeClass("x-tree-selected");
33898         }
33899     },*/
33900     addClass : function(cls){
33901         if(this.elRow){
33902             Roo.fly(this.elRow).addClass(cls);
33903         }
33904         
33905     },
33906     
33907     
33908     removeClass : function(cls){
33909         if(this.elRow){
33910             Roo.fly(this.elRow).removeClass(cls);
33911         }
33912     }
33913
33914     
33915     
33916 });//<Script type="text/javascript">
33917
33918 /*
33919  * Based on:
33920  * Ext JS Library 1.1.1
33921  * Copyright(c) 2006-2007, Ext JS, LLC.
33922  *
33923  * Originally Released Under LGPL - original licence link has changed is not relivant.
33924  *
33925  * Fork - LGPL
33926  * <script type="text/javascript">
33927  */
33928  
33929
33930 /**
33931  * @class Roo.tree.ColumnTree
33932  * @extends Roo.data.TreePanel
33933  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33934  * @cfg {int} borderWidth  compined right/left border allowance
33935  * @constructor
33936  * @param {String/HTMLElement/Element} el The container element
33937  * @param {Object} config
33938  */
33939 Roo.tree.ColumnTree =  function(el, config)
33940 {
33941    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33942    this.addEvents({
33943         /**
33944         * @event resize
33945         * Fire this event on a container when it resizes
33946         * @param {int} w Width
33947         * @param {int} h Height
33948         */
33949        "resize" : true
33950     });
33951     this.on('resize', this.onResize, this);
33952 };
33953
33954 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33955     //lines:false,
33956     
33957     
33958     borderWidth: Roo.isBorderBox ? 0 : 2, 
33959     headEls : false,
33960     
33961     render : function(){
33962         // add the header.....
33963        
33964         Roo.tree.ColumnTree.superclass.render.apply(this);
33965         
33966         this.el.addClass('x-column-tree');
33967         
33968         this.headers = this.el.createChild(
33969             {cls:'x-tree-headers'},this.innerCt.dom);
33970    
33971         var cols = this.columns, c;
33972         var totalWidth = 0;
33973         this.headEls = [];
33974         var  len = cols.length;
33975         for(var i = 0; i < len; i++){
33976              c = cols[i];
33977              totalWidth += c.width;
33978             this.headEls.push(this.headers.createChild({
33979                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33980                  cn: {
33981                      cls:'x-tree-hd-text',
33982                      html: c.header
33983                  },
33984                  style:'width:'+(c.width-this.borderWidth)+'px;'
33985              }));
33986         }
33987         this.headers.createChild({cls:'x-clear'});
33988         // prevent floats from wrapping when clipped
33989         this.headers.setWidth(totalWidth);
33990         //this.innerCt.setWidth(totalWidth);
33991         this.innerCt.setStyle({ overflow: 'auto' });
33992         this.onResize(this.width, this.height);
33993              
33994         
33995     },
33996     onResize : function(w,h)
33997     {
33998         this.height = h;
33999         this.width = w;
34000         // resize cols..
34001         this.innerCt.setWidth(this.width);
34002         this.innerCt.setHeight(this.height-20);
34003         
34004         // headers...
34005         var cols = this.columns, c;
34006         var totalWidth = 0;
34007         var expEl = false;
34008         var len = cols.length;
34009         for(var i = 0; i < len; i++){
34010             c = cols[i];
34011             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
34012                 // it's the expander..
34013                 expEl  = this.headEls[i];
34014                 continue;
34015             }
34016             totalWidth += c.width;
34017             
34018         }
34019         if (expEl) {
34020             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34021         }
34022         this.headers.setWidth(w-20);
34023
34024         
34025         
34026         
34027     }
34028 });
34029 /*
34030  * Based on:
34031  * Ext JS Library 1.1.1
34032  * Copyright(c) 2006-2007, Ext JS, LLC.
34033  *
34034  * Originally Released Under LGPL - original licence link has changed is not relivant.
34035  *
34036  * Fork - LGPL
34037  * <script type="text/javascript">
34038  */
34039  
34040 /**
34041  * @class Roo.menu.Menu
34042  * @extends Roo.util.Observable
34043  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34044  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34045  * @constructor
34046  * Creates a new Menu
34047  * @param {Object} config Configuration options
34048  */
34049 Roo.menu.Menu = function(config){
34050     Roo.apply(this, config);
34051     this.id = this.id || Roo.id();
34052     this.addEvents({
34053         /**
34054          * @event beforeshow
34055          * Fires before this menu is displayed
34056          * @param {Roo.menu.Menu} this
34057          */
34058         beforeshow : true,
34059         /**
34060          * @event beforehide
34061          * Fires before this menu is hidden
34062          * @param {Roo.menu.Menu} this
34063          */
34064         beforehide : true,
34065         /**
34066          * @event show
34067          * Fires after this menu is displayed
34068          * @param {Roo.menu.Menu} this
34069          */
34070         show : true,
34071         /**
34072          * @event hide
34073          * Fires after this menu is hidden
34074          * @param {Roo.menu.Menu} this
34075          */
34076         hide : true,
34077         /**
34078          * @event click
34079          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34080          * @param {Roo.menu.Menu} this
34081          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34082          * @param {Roo.EventObject} e
34083          */
34084         click : true,
34085         /**
34086          * @event mouseover
34087          * Fires when the mouse is hovering over this menu
34088          * @param {Roo.menu.Menu} this
34089          * @param {Roo.EventObject} e
34090          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34091          */
34092         mouseover : true,
34093         /**
34094          * @event mouseout
34095          * Fires when the mouse exits this menu
34096          * @param {Roo.menu.Menu} this
34097          * @param {Roo.EventObject} e
34098          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34099          */
34100         mouseout : true,
34101         /**
34102          * @event itemclick
34103          * Fires when a menu item contained in this menu is clicked
34104          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34105          * @param {Roo.EventObject} e
34106          */
34107         itemclick: true
34108     });
34109     if (this.registerMenu) {
34110         Roo.menu.MenuMgr.register(this);
34111     }
34112     
34113     var mis = this.items;
34114     this.items = new Roo.util.MixedCollection();
34115     if(mis){
34116         this.add.apply(this, mis);
34117     }
34118 };
34119
34120 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34121     /**
34122      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34123      */
34124     minWidth : 120,
34125     /**
34126      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34127      * for bottom-right shadow (defaults to "sides")
34128      */
34129     shadow : "sides",
34130     /**
34131      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34132      * this menu (defaults to "tl-tr?")
34133      */
34134     subMenuAlign : "tl-tr?",
34135     /**
34136      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34137      * relative to its element of origin (defaults to "tl-bl?")
34138      */
34139     defaultAlign : "tl-bl?",
34140     /**
34141      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34142      */
34143     allowOtherMenus : false,
34144     /**
34145      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34146      */
34147     registerMenu : true,
34148
34149     hidden:true,
34150
34151     // private
34152     render : function(){
34153         if(this.el){
34154             return;
34155         }
34156         var el = this.el = new Roo.Layer({
34157             cls: "x-menu",
34158             shadow:this.shadow,
34159             constrain: false,
34160             parentEl: this.parentEl || document.body,
34161             zindex:15000
34162         });
34163
34164         this.keyNav = new Roo.menu.MenuNav(this);
34165
34166         if(this.plain){
34167             el.addClass("x-menu-plain");
34168         }
34169         if(this.cls){
34170             el.addClass(this.cls);
34171         }
34172         // generic focus element
34173         this.focusEl = el.createChild({
34174             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34175         });
34176         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34177         ul.on("click", this.onClick, this);
34178         ul.on("mouseover", this.onMouseOver, this);
34179         ul.on("mouseout", this.onMouseOut, this);
34180         this.items.each(function(item){
34181             var li = document.createElement("li");
34182             li.className = "x-menu-list-item";
34183             ul.dom.appendChild(li);
34184             item.render(li, this);
34185         }, this);
34186         this.ul = ul;
34187         this.autoWidth();
34188     },
34189
34190     // private
34191     autoWidth : function(){
34192         var el = this.el, ul = this.ul;
34193         if(!el){
34194             return;
34195         }
34196         var w = this.width;
34197         if(w){
34198             el.setWidth(w);
34199         }else if(Roo.isIE){
34200             el.setWidth(this.minWidth);
34201             var t = el.dom.offsetWidth; // force recalc
34202             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34203         }
34204     },
34205
34206     // private
34207     delayAutoWidth : function(){
34208         if(this.rendered){
34209             if(!this.awTask){
34210                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34211             }
34212             this.awTask.delay(20);
34213         }
34214     },
34215
34216     // private
34217     findTargetItem : function(e){
34218         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34219         if(t && t.menuItemId){
34220             return this.items.get(t.menuItemId);
34221         }
34222     },
34223
34224     // private
34225     onClick : function(e){
34226         var t;
34227         if(t = this.findTargetItem(e)){
34228             t.onClick(e);
34229             this.fireEvent("click", this, t, e);
34230         }
34231     },
34232
34233     // private
34234     setActiveItem : function(item, autoExpand){
34235         if(item != this.activeItem){
34236             if(this.activeItem){
34237                 this.activeItem.deactivate();
34238             }
34239             this.activeItem = item;
34240             item.activate(autoExpand);
34241         }else if(autoExpand){
34242             item.expandMenu();
34243         }
34244     },
34245
34246     // private
34247     tryActivate : function(start, step){
34248         var items = this.items;
34249         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34250             var item = items.get(i);
34251             if(!item.disabled && item.canActivate){
34252                 this.setActiveItem(item, false);
34253                 return item;
34254             }
34255         }
34256         return false;
34257     },
34258
34259     // private
34260     onMouseOver : function(e){
34261         var t;
34262         if(t = this.findTargetItem(e)){
34263             if(t.canActivate && !t.disabled){
34264                 this.setActiveItem(t, true);
34265             }
34266         }
34267         this.fireEvent("mouseover", this, e, t);
34268     },
34269
34270     // private
34271     onMouseOut : function(e){
34272         var t;
34273         if(t = this.findTargetItem(e)){
34274             if(t == this.activeItem && t.shouldDeactivate(e)){
34275                 this.activeItem.deactivate();
34276                 delete this.activeItem;
34277             }
34278         }
34279         this.fireEvent("mouseout", this, e, t);
34280     },
34281
34282     /**
34283      * Read-only.  Returns true if the menu is currently displayed, else false.
34284      * @type Boolean
34285      */
34286     isVisible : function(){
34287         return this.el && !this.hidden;
34288     },
34289
34290     /**
34291      * Displays this menu relative to another element
34292      * @param {String/HTMLElement/Roo.Element} element The element to align to
34293      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34294      * the element (defaults to this.defaultAlign)
34295      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34296      */
34297     show : function(el, pos, parentMenu){
34298         this.parentMenu = parentMenu;
34299         if(!this.el){
34300             this.render();
34301         }
34302         this.fireEvent("beforeshow", this);
34303         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34304     },
34305
34306     /**
34307      * Displays this menu at a specific xy position
34308      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34309      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34310      */
34311     showAt : function(xy, parentMenu, /* private: */_e){
34312         this.parentMenu = parentMenu;
34313         if(!this.el){
34314             this.render();
34315         }
34316         if(_e !== false){
34317             this.fireEvent("beforeshow", this);
34318             xy = this.el.adjustForConstraints(xy);
34319         }
34320         this.el.setXY(xy);
34321         this.el.show();
34322         this.hidden = false;
34323         this.focus();
34324         this.fireEvent("show", this);
34325     },
34326
34327     focus : function(){
34328         if(!this.hidden){
34329             this.doFocus.defer(50, this);
34330         }
34331     },
34332
34333     doFocus : function(){
34334         if(!this.hidden){
34335             this.focusEl.focus();
34336         }
34337     },
34338
34339     /**
34340      * Hides this menu and optionally all parent menus
34341      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34342      */
34343     hide : function(deep){
34344         if(this.el && this.isVisible()){
34345             this.fireEvent("beforehide", this);
34346             if(this.activeItem){
34347                 this.activeItem.deactivate();
34348                 this.activeItem = null;
34349             }
34350             this.el.hide();
34351             this.hidden = true;
34352             this.fireEvent("hide", this);
34353         }
34354         if(deep === true && this.parentMenu){
34355             this.parentMenu.hide(true);
34356         }
34357     },
34358
34359     /**
34360      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34361      * Any of the following are valid:
34362      * <ul>
34363      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34364      * <li>An HTMLElement object which will be converted to a menu item</li>
34365      * <li>A menu item config object that will be created as a new menu item</li>
34366      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34367      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34368      * </ul>
34369      * Usage:
34370      * <pre><code>
34371 // Create the menu
34372 var menu = new Roo.menu.Menu();
34373
34374 // Create a menu item to add by reference
34375 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34376
34377 // Add a bunch of items at once using different methods.
34378 // Only the last item added will be returned.
34379 var item = menu.add(
34380     menuItem,                // add existing item by ref
34381     'Dynamic Item',          // new TextItem
34382     '-',                     // new separator
34383     { text: 'Config Item' }  // new item by config
34384 );
34385 </code></pre>
34386      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34387      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34388      */
34389     add : function(){
34390         var a = arguments, l = a.length, item;
34391         for(var i = 0; i < l; i++){
34392             var el = a[i];
34393             if ((typeof(el) == "object") && el.xtype && el.xns) {
34394                 el = Roo.factory(el, Roo.menu);
34395             }
34396             
34397             if(el.render){ // some kind of Item
34398                 item = this.addItem(el);
34399             }else if(typeof el == "string"){ // string
34400                 if(el == "separator" || el == "-"){
34401                     item = this.addSeparator();
34402                 }else{
34403                     item = this.addText(el);
34404                 }
34405             }else if(el.tagName || el.el){ // element
34406                 item = this.addElement(el);
34407             }else if(typeof el == "object"){ // must be menu item config?
34408                 item = this.addMenuItem(el);
34409             }
34410         }
34411         return item;
34412     },
34413
34414     /**
34415      * Returns this menu's underlying {@link Roo.Element} object
34416      * @return {Roo.Element} The element
34417      */
34418     getEl : function(){
34419         if(!this.el){
34420             this.render();
34421         }
34422         return this.el;
34423     },
34424
34425     /**
34426      * Adds a separator bar to the menu
34427      * @return {Roo.menu.Item} The menu item that was added
34428      */
34429     addSeparator : function(){
34430         return this.addItem(new Roo.menu.Separator());
34431     },
34432
34433     /**
34434      * Adds an {@link Roo.Element} object to the menu
34435      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34436      * @return {Roo.menu.Item} The menu item that was added
34437      */
34438     addElement : function(el){
34439         return this.addItem(new Roo.menu.BaseItem(el));
34440     },
34441
34442     /**
34443      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34444      * @param {Roo.menu.Item} item The menu item to add
34445      * @return {Roo.menu.Item} The menu item that was added
34446      */
34447     addItem : function(item){
34448         this.items.add(item);
34449         if(this.ul){
34450             var li = document.createElement("li");
34451             li.className = "x-menu-list-item";
34452             this.ul.dom.appendChild(li);
34453             item.render(li, this);
34454             this.delayAutoWidth();
34455         }
34456         return item;
34457     },
34458
34459     /**
34460      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34461      * @param {Object} config A MenuItem config object
34462      * @return {Roo.menu.Item} The menu item that was added
34463      */
34464     addMenuItem : function(config){
34465         if(!(config instanceof Roo.menu.Item)){
34466             if(typeof config.checked == "boolean"){ // must be check menu item config?
34467                 config = new Roo.menu.CheckItem(config);
34468             }else{
34469                 config = new Roo.menu.Item(config);
34470             }
34471         }
34472         return this.addItem(config);
34473     },
34474
34475     /**
34476      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34477      * @param {String} text The text to display in the menu item
34478      * @return {Roo.menu.Item} The menu item that was added
34479      */
34480     addText : function(text){
34481         return this.addItem(new Roo.menu.TextItem({ text : text }));
34482     },
34483
34484     /**
34485      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34486      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34487      * @param {Roo.menu.Item} item The menu item to add
34488      * @return {Roo.menu.Item} The menu item that was added
34489      */
34490     insert : function(index, item){
34491         this.items.insert(index, item);
34492         if(this.ul){
34493             var li = document.createElement("li");
34494             li.className = "x-menu-list-item";
34495             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34496             item.render(li, this);
34497             this.delayAutoWidth();
34498         }
34499         return item;
34500     },
34501
34502     /**
34503      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34504      * @param {Roo.menu.Item} item The menu item to remove
34505      */
34506     remove : function(item){
34507         this.items.removeKey(item.id);
34508         item.destroy();
34509     },
34510
34511     /**
34512      * Removes and destroys all items in the menu
34513      */
34514     removeAll : function(){
34515         var f;
34516         while(f = this.items.first()){
34517             this.remove(f);
34518         }
34519     }
34520 });
34521
34522 // MenuNav is a private utility class used internally by the Menu
34523 Roo.menu.MenuNav = function(menu){
34524     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34525     this.scope = this.menu = menu;
34526 };
34527
34528 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34529     doRelay : function(e, h){
34530         var k = e.getKey();
34531         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34532             this.menu.tryActivate(0, 1);
34533             return false;
34534         }
34535         return h.call(this.scope || this, e, this.menu);
34536     },
34537
34538     up : function(e, m){
34539         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34540             m.tryActivate(m.items.length-1, -1);
34541         }
34542     },
34543
34544     down : function(e, m){
34545         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34546             m.tryActivate(0, 1);
34547         }
34548     },
34549
34550     right : function(e, m){
34551         if(m.activeItem){
34552             m.activeItem.expandMenu(true);
34553         }
34554     },
34555
34556     left : function(e, m){
34557         m.hide();
34558         if(m.parentMenu && m.parentMenu.activeItem){
34559             m.parentMenu.activeItem.activate();
34560         }
34561     },
34562
34563     enter : function(e, m){
34564         if(m.activeItem){
34565             e.stopPropagation();
34566             m.activeItem.onClick(e);
34567             m.fireEvent("click", this, m.activeItem);
34568             return true;
34569         }
34570     }
34571 });/*
34572  * Based on:
34573  * Ext JS Library 1.1.1
34574  * Copyright(c) 2006-2007, Ext JS, LLC.
34575  *
34576  * Originally Released Under LGPL - original licence link has changed is not relivant.
34577  *
34578  * Fork - LGPL
34579  * <script type="text/javascript">
34580  */
34581  
34582 /**
34583  * @class Roo.menu.MenuMgr
34584  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34585  * @singleton
34586  */
34587 Roo.menu.MenuMgr = function(){
34588    var menus, active, groups = {}, attached = false, lastShow = new Date();
34589
34590    // private - called when first menu is created
34591    function init(){
34592        menus = {};
34593        active = new Roo.util.MixedCollection();
34594        Roo.get(document).addKeyListener(27, function(){
34595            if(active.length > 0){
34596                hideAll();
34597            }
34598        });
34599    }
34600
34601    // private
34602    function hideAll(){
34603        if(active && active.length > 0){
34604            var c = active.clone();
34605            c.each(function(m){
34606                m.hide();
34607            });
34608        }
34609    }
34610
34611    // private
34612    function onHide(m){
34613        active.remove(m);
34614        if(active.length < 1){
34615            Roo.get(document).un("mousedown", onMouseDown);
34616            attached = false;
34617        }
34618    }
34619
34620    // private
34621    function onShow(m){
34622        var last = active.last();
34623        lastShow = new Date();
34624        active.add(m);
34625        if(!attached){
34626            Roo.get(document).on("mousedown", onMouseDown);
34627            attached = true;
34628        }
34629        if(m.parentMenu){
34630           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34631           m.parentMenu.activeChild = m;
34632        }else if(last && last.isVisible()){
34633           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34634        }
34635    }
34636
34637    // private
34638    function onBeforeHide(m){
34639        if(m.activeChild){
34640            m.activeChild.hide();
34641        }
34642        if(m.autoHideTimer){
34643            clearTimeout(m.autoHideTimer);
34644            delete m.autoHideTimer;
34645        }
34646    }
34647
34648    // private
34649    function onBeforeShow(m){
34650        var pm = m.parentMenu;
34651        if(!pm && !m.allowOtherMenus){
34652            hideAll();
34653        }else if(pm && pm.activeChild && active != m){
34654            pm.activeChild.hide();
34655        }
34656    }
34657
34658    // private
34659    function onMouseDown(e){
34660        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34661            hideAll();
34662        }
34663    }
34664
34665    // private
34666    function onBeforeCheck(mi, state){
34667        if(state){
34668            var g = groups[mi.group];
34669            for(var i = 0, l = g.length; i < l; i++){
34670                if(g[i] != mi){
34671                    g[i].setChecked(false);
34672                }
34673            }
34674        }
34675    }
34676
34677    return {
34678
34679        /**
34680         * Hides all menus that are currently visible
34681         */
34682        hideAll : function(){
34683             hideAll();  
34684        },
34685
34686        // private
34687        register : function(menu){
34688            if(!menus){
34689                init();
34690            }
34691            menus[menu.id] = menu;
34692            menu.on("beforehide", onBeforeHide);
34693            menu.on("hide", onHide);
34694            menu.on("beforeshow", onBeforeShow);
34695            menu.on("show", onShow);
34696            var g = menu.group;
34697            if(g && menu.events["checkchange"]){
34698                if(!groups[g]){
34699                    groups[g] = [];
34700                }
34701                groups[g].push(menu);
34702                menu.on("checkchange", onCheck);
34703            }
34704        },
34705
34706         /**
34707          * Returns a {@link Roo.menu.Menu} object
34708          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34709          * be used to generate and return a new Menu instance.
34710          */
34711        get : function(menu){
34712            if(typeof menu == "string"){ // menu id
34713                return menus[menu];
34714            }else if(menu.events){  // menu instance
34715                return menu;
34716            }else if(typeof menu.length == 'number'){ // array of menu items?
34717                return new Roo.menu.Menu({items:menu});
34718            }else{ // otherwise, must be a config
34719                return new Roo.menu.Menu(menu);
34720            }
34721        },
34722
34723        // private
34724        unregister : function(menu){
34725            delete menus[menu.id];
34726            menu.un("beforehide", onBeforeHide);
34727            menu.un("hide", onHide);
34728            menu.un("beforeshow", onBeforeShow);
34729            menu.un("show", onShow);
34730            var g = menu.group;
34731            if(g && menu.events["checkchange"]){
34732                groups[g].remove(menu);
34733                menu.un("checkchange", onCheck);
34734            }
34735        },
34736
34737        // private
34738        registerCheckable : function(menuItem){
34739            var g = menuItem.group;
34740            if(g){
34741                if(!groups[g]){
34742                    groups[g] = [];
34743                }
34744                groups[g].push(menuItem);
34745                menuItem.on("beforecheckchange", onBeforeCheck);
34746            }
34747        },
34748
34749        // private
34750        unregisterCheckable : function(menuItem){
34751            var g = menuItem.group;
34752            if(g){
34753                groups[g].remove(menuItem);
34754                menuItem.un("beforecheckchange", onBeforeCheck);
34755            }
34756        }
34757    };
34758 }();/*
34759  * Based on:
34760  * Ext JS Library 1.1.1
34761  * Copyright(c) 2006-2007, Ext JS, LLC.
34762  *
34763  * Originally Released Under LGPL - original licence link has changed is not relivant.
34764  *
34765  * Fork - LGPL
34766  * <script type="text/javascript">
34767  */
34768  
34769
34770 /**
34771  * @class Roo.menu.BaseItem
34772  * @extends Roo.Component
34773  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34774  * management and base configuration options shared by all menu components.
34775  * @constructor
34776  * Creates a new BaseItem
34777  * @param {Object} config Configuration options
34778  */
34779 Roo.menu.BaseItem = function(config){
34780     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34781
34782     this.addEvents({
34783         /**
34784          * @event click
34785          * Fires when this item is clicked
34786          * @param {Roo.menu.BaseItem} this
34787          * @param {Roo.EventObject} e
34788          */
34789         click: true,
34790         /**
34791          * @event activate
34792          * Fires when this item is activated
34793          * @param {Roo.menu.BaseItem} this
34794          */
34795         activate : true,
34796         /**
34797          * @event deactivate
34798          * Fires when this item is deactivated
34799          * @param {Roo.menu.BaseItem} this
34800          */
34801         deactivate : true
34802     });
34803
34804     if(this.handler){
34805         this.on("click", this.handler, this.scope, true);
34806     }
34807 };
34808
34809 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34810     /**
34811      * @cfg {Function} handler
34812      * A function that will handle the click event of this menu item (defaults to undefined)
34813      */
34814     /**
34815      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34816      */
34817     canActivate : false,
34818     /**
34819      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34820      */
34821     activeClass : "x-menu-item-active",
34822     /**
34823      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34824      */
34825     hideOnClick : true,
34826     /**
34827      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34828      */
34829     hideDelay : 100,
34830
34831     // private
34832     ctype: "Roo.menu.BaseItem",
34833
34834     // private
34835     actionMode : "container",
34836
34837     // private
34838     render : function(container, parentMenu){
34839         this.parentMenu = parentMenu;
34840         Roo.menu.BaseItem.superclass.render.call(this, container);
34841         this.container.menuItemId = this.id;
34842     },
34843
34844     // private
34845     onRender : function(container, position){
34846         this.el = Roo.get(this.el);
34847         container.dom.appendChild(this.el.dom);
34848     },
34849
34850     // private
34851     onClick : function(e){
34852         if(!this.disabled && this.fireEvent("click", this, e) !== false
34853                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34854             this.handleClick(e);
34855         }else{
34856             e.stopEvent();
34857         }
34858     },
34859
34860     // private
34861     activate : function(){
34862         if(this.disabled){
34863             return false;
34864         }
34865         var li = this.container;
34866         li.addClass(this.activeClass);
34867         this.region = li.getRegion().adjust(2, 2, -2, -2);
34868         this.fireEvent("activate", this);
34869         return true;
34870     },
34871
34872     // private
34873     deactivate : function(){
34874         this.container.removeClass(this.activeClass);
34875         this.fireEvent("deactivate", this);
34876     },
34877
34878     // private
34879     shouldDeactivate : function(e){
34880         return !this.region || !this.region.contains(e.getPoint());
34881     },
34882
34883     // private
34884     handleClick : function(e){
34885         if(this.hideOnClick){
34886             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34887         }
34888     },
34889
34890     // private
34891     expandMenu : function(autoActivate){
34892         // do nothing
34893     },
34894
34895     // private
34896     hideMenu : function(){
34897         // do nothing
34898     }
34899 });/*
34900  * Based on:
34901  * Ext JS Library 1.1.1
34902  * Copyright(c) 2006-2007, Ext JS, LLC.
34903  *
34904  * Originally Released Under LGPL - original licence link has changed is not relivant.
34905  *
34906  * Fork - LGPL
34907  * <script type="text/javascript">
34908  */
34909  
34910 /**
34911  * @class Roo.menu.Adapter
34912  * @extends Roo.menu.BaseItem
34913  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
34914  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34915  * @constructor
34916  * Creates a new Adapter
34917  * @param {Object} config Configuration options
34918  */
34919 Roo.menu.Adapter = function(component, config){
34920     Roo.menu.Adapter.superclass.constructor.call(this, config);
34921     this.component = component;
34922 };
34923 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34924     // private
34925     canActivate : true,
34926
34927     // private
34928     onRender : function(container, position){
34929         this.component.render(container);
34930         this.el = this.component.getEl();
34931     },
34932
34933     // private
34934     activate : function(){
34935         if(this.disabled){
34936             return false;
34937         }
34938         this.component.focus();
34939         this.fireEvent("activate", this);
34940         return true;
34941     },
34942
34943     // private
34944     deactivate : function(){
34945         this.fireEvent("deactivate", this);
34946     },
34947
34948     // private
34949     disable : function(){
34950         this.component.disable();
34951         Roo.menu.Adapter.superclass.disable.call(this);
34952     },
34953
34954     // private
34955     enable : function(){
34956         this.component.enable();
34957         Roo.menu.Adapter.superclass.enable.call(this);
34958     }
34959 });/*
34960  * Based on:
34961  * Ext JS Library 1.1.1
34962  * Copyright(c) 2006-2007, Ext JS, LLC.
34963  *
34964  * Originally Released Under LGPL - original licence link has changed is not relivant.
34965  *
34966  * Fork - LGPL
34967  * <script type="text/javascript">
34968  */
34969
34970 /**
34971  * @class Roo.menu.TextItem
34972  * @extends Roo.menu.BaseItem
34973  * Adds a static text string to a menu, usually used as either a heading or group separator.
34974  * Note: old style constructor with text is still supported.
34975  * 
34976  * @constructor
34977  * Creates a new TextItem
34978  * @param {Object} cfg Configuration
34979  */
34980 Roo.menu.TextItem = function(cfg){
34981     if (typeof(cfg) == 'string') {
34982         this.text = cfg;
34983     } else {
34984         Roo.apply(this,cfg);
34985     }
34986     
34987     Roo.menu.TextItem.superclass.constructor.call(this);
34988 };
34989
34990 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34991     /**
34992      * @cfg {Boolean} text Text to show on item.
34993      */
34994     text : '',
34995     
34996     /**
34997      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34998      */
34999     hideOnClick : false,
35000     /**
35001      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
35002      */
35003     itemCls : "x-menu-text",
35004
35005     // private
35006     onRender : function(){
35007         var s = document.createElement("span");
35008         s.className = this.itemCls;
35009         s.innerHTML = this.text;
35010         this.el = s;
35011         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
35012     }
35013 });/*
35014  * Based on:
35015  * Ext JS Library 1.1.1
35016  * Copyright(c) 2006-2007, Ext JS, LLC.
35017  *
35018  * Originally Released Under LGPL - original licence link has changed is not relivant.
35019  *
35020  * Fork - LGPL
35021  * <script type="text/javascript">
35022  */
35023
35024 /**
35025  * @class Roo.menu.Separator
35026  * @extends Roo.menu.BaseItem
35027  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35028  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35029  * @constructor
35030  * @param {Object} config Configuration options
35031  */
35032 Roo.menu.Separator = function(config){
35033     Roo.menu.Separator.superclass.constructor.call(this, config);
35034 };
35035
35036 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35037     /**
35038      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35039      */
35040     itemCls : "x-menu-sep",
35041     /**
35042      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35043      */
35044     hideOnClick : false,
35045
35046     // private
35047     onRender : function(li){
35048         var s = document.createElement("span");
35049         s.className = this.itemCls;
35050         s.innerHTML = "&#160;";
35051         this.el = s;
35052         li.addClass("x-menu-sep-li");
35053         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35054     }
35055 });/*
35056  * Based on:
35057  * Ext JS Library 1.1.1
35058  * Copyright(c) 2006-2007, Ext JS, LLC.
35059  *
35060  * Originally Released Under LGPL - original licence link has changed is not relivant.
35061  *
35062  * Fork - LGPL
35063  * <script type="text/javascript">
35064  */
35065 /**
35066  * @class Roo.menu.Item
35067  * @extends Roo.menu.BaseItem
35068  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35069  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35070  * activation and click handling.
35071  * @constructor
35072  * Creates a new Item
35073  * @param {Object} config Configuration options
35074  */
35075 Roo.menu.Item = function(config){
35076     Roo.menu.Item.superclass.constructor.call(this, config);
35077     if(this.menu){
35078         this.menu = Roo.menu.MenuMgr.get(this.menu);
35079     }
35080 };
35081 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35082     
35083     /**
35084      * @cfg {String} text
35085      * The text to show on the menu item.
35086      */
35087     text: '',
35088      /**
35089      * @cfg {String} HTML to render in menu
35090      * The text to show on the menu item (HTML version).
35091      */
35092     html: '',
35093     /**
35094      * @cfg {String} icon
35095      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35096      */
35097     icon: undefined,
35098     /**
35099      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35100      */
35101     itemCls : "x-menu-item",
35102     /**
35103      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35104      */
35105     canActivate : true,
35106     /**
35107      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35108      */
35109     showDelay: 200,
35110     // doc'd in BaseItem
35111     hideDelay: 200,
35112
35113     // private
35114     ctype: "Roo.menu.Item",
35115     
35116     // private
35117     onRender : function(container, position){
35118         var el = document.createElement("a");
35119         el.hideFocus = true;
35120         el.unselectable = "on";
35121         el.href = this.href || "#";
35122         if(this.hrefTarget){
35123             el.target = this.hrefTarget;
35124         }
35125         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35126         
35127         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35128         
35129         el.innerHTML = String.format(
35130                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35131                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35132         this.el = el;
35133         Roo.menu.Item.superclass.onRender.call(this, container, position);
35134     },
35135
35136     /**
35137      * Sets the text to display in this menu item
35138      * @param {String} text The text to display
35139      * @param {Boolean} isHTML true to indicate text is pure html.
35140      */
35141     setText : function(text, isHTML){
35142         if (isHTML) {
35143             this.html = text;
35144         } else {
35145             this.text = text;
35146             this.html = '';
35147         }
35148         if(this.rendered){
35149             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35150      
35151             this.el.update(String.format(
35152                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35153                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35154             this.parentMenu.autoWidth();
35155         }
35156     },
35157
35158     // private
35159     handleClick : function(e){
35160         if(!this.href){ // if no link defined, stop the event automatically
35161             e.stopEvent();
35162         }
35163         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35164     },
35165
35166     // private
35167     activate : function(autoExpand){
35168         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35169             this.focus();
35170             if(autoExpand){
35171                 this.expandMenu();
35172             }
35173         }
35174         return true;
35175     },
35176
35177     // private
35178     shouldDeactivate : function(e){
35179         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35180             if(this.menu && this.menu.isVisible()){
35181                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35182             }
35183             return true;
35184         }
35185         return false;
35186     },
35187
35188     // private
35189     deactivate : function(){
35190         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35191         this.hideMenu();
35192     },
35193
35194     // private
35195     expandMenu : function(autoActivate){
35196         if(!this.disabled && this.menu){
35197             clearTimeout(this.hideTimer);
35198             delete this.hideTimer;
35199             if(!this.menu.isVisible() && !this.showTimer){
35200                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35201             }else if (this.menu.isVisible() && autoActivate){
35202                 this.menu.tryActivate(0, 1);
35203             }
35204         }
35205     },
35206
35207     // private
35208     deferExpand : function(autoActivate){
35209         delete this.showTimer;
35210         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35211         if(autoActivate){
35212             this.menu.tryActivate(0, 1);
35213         }
35214     },
35215
35216     // private
35217     hideMenu : function(){
35218         clearTimeout(this.showTimer);
35219         delete this.showTimer;
35220         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35221             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35222         }
35223     },
35224
35225     // private
35226     deferHide : function(){
35227         delete this.hideTimer;
35228         this.menu.hide();
35229     }
35230 });/*
35231  * Based on:
35232  * Ext JS Library 1.1.1
35233  * Copyright(c) 2006-2007, Ext JS, LLC.
35234  *
35235  * Originally Released Under LGPL - original licence link has changed is not relivant.
35236  *
35237  * Fork - LGPL
35238  * <script type="text/javascript">
35239  */
35240  
35241 /**
35242  * @class Roo.menu.CheckItem
35243  * @extends Roo.menu.Item
35244  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35245  * @constructor
35246  * Creates a new CheckItem
35247  * @param {Object} config Configuration options
35248  */
35249 Roo.menu.CheckItem = function(config){
35250     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35251     this.addEvents({
35252         /**
35253          * @event beforecheckchange
35254          * Fires before the checked value is set, providing an opportunity to cancel if needed
35255          * @param {Roo.menu.CheckItem} this
35256          * @param {Boolean} checked The new checked value that will be set
35257          */
35258         "beforecheckchange" : true,
35259         /**
35260          * @event checkchange
35261          * Fires after the checked value has been set
35262          * @param {Roo.menu.CheckItem} this
35263          * @param {Boolean} checked The checked value that was set
35264          */
35265         "checkchange" : true
35266     });
35267     if(this.checkHandler){
35268         this.on('checkchange', this.checkHandler, this.scope);
35269     }
35270 };
35271 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35272     /**
35273      * @cfg {String} group
35274      * All check items with the same group name will automatically be grouped into a single-select
35275      * radio button group (defaults to '')
35276      */
35277     /**
35278      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35279      */
35280     itemCls : "x-menu-item x-menu-check-item",
35281     /**
35282      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35283      */
35284     groupClass : "x-menu-group-item",
35285
35286     /**
35287      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35288      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35289      * initialized with checked = true will be rendered as checked.
35290      */
35291     checked: false,
35292
35293     // private
35294     ctype: "Roo.menu.CheckItem",
35295
35296     // private
35297     onRender : function(c){
35298         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35299         if(this.group){
35300             this.el.addClass(this.groupClass);
35301         }
35302         Roo.menu.MenuMgr.registerCheckable(this);
35303         if(this.checked){
35304             this.checked = false;
35305             this.setChecked(true, true);
35306         }
35307     },
35308
35309     // private
35310     destroy : function(){
35311         if(this.rendered){
35312             Roo.menu.MenuMgr.unregisterCheckable(this);
35313         }
35314         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35315     },
35316
35317     /**
35318      * Set the checked state of this item
35319      * @param {Boolean} checked The new checked value
35320      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35321      */
35322     setChecked : function(state, suppressEvent){
35323         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35324             if(this.container){
35325                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35326             }
35327             this.checked = state;
35328             if(suppressEvent !== true){
35329                 this.fireEvent("checkchange", this, state);
35330             }
35331         }
35332     },
35333
35334     // private
35335     handleClick : function(e){
35336        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35337            this.setChecked(!this.checked);
35338        }
35339        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35340     }
35341 });/*
35342  * Based on:
35343  * Ext JS Library 1.1.1
35344  * Copyright(c) 2006-2007, Ext JS, LLC.
35345  *
35346  * Originally Released Under LGPL - original licence link has changed is not relivant.
35347  *
35348  * Fork - LGPL
35349  * <script type="text/javascript">
35350  */
35351  
35352 /**
35353  * @class Roo.menu.DateItem
35354  * @extends Roo.menu.Adapter
35355  * A menu item that wraps the {@link Roo.DatPicker} component.
35356  * @constructor
35357  * Creates a new DateItem
35358  * @param {Object} config Configuration options
35359  */
35360 Roo.menu.DateItem = function(config){
35361     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35362     /** The Roo.DatePicker object @type Roo.DatePicker */
35363     this.picker = this.component;
35364     this.addEvents({select: true});
35365     
35366     this.picker.on("render", function(picker){
35367         picker.getEl().swallowEvent("click");
35368         picker.container.addClass("x-menu-date-item");
35369     });
35370
35371     this.picker.on("select", this.onSelect, this);
35372 };
35373
35374 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35375     // private
35376     onSelect : function(picker, date){
35377         this.fireEvent("select", this, date, picker);
35378         Roo.menu.DateItem.superclass.handleClick.call(this);
35379     }
35380 });/*
35381  * Based on:
35382  * Ext JS Library 1.1.1
35383  * Copyright(c) 2006-2007, Ext JS, LLC.
35384  *
35385  * Originally Released Under LGPL - original licence link has changed is not relivant.
35386  *
35387  * Fork - LGPL
35388  * <script type="text/javascript">
35389  */
35390  
35391 /**
35392  * @class Roo.menu.ColorItem
35393  * @extends Roo.menu.Adapter
35394  * A menu item that wraps the {@link Roo.ColorPalette} component.
35395  * @constructor
35396  * Creates a new ColorItem
35397  * @param {Object} config Configuration options
35398  */
35399 Roo.menu.ColorItem = function(config){
35400     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35401     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35402     this.palette = this.component;
35403     this.relayEvents(this.palette, ["select"]);
35404     if(this.selectHandler){
35405         this.on('select', this.selectHandler, this.scope);
35406     }
35407 };
35408 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35409  * Based on:
35410  * Ext JS Library 1.1.1
35411  * Copyright(c) 2006-2007, Ext JS, LLC.
35412  *
35413  * Originally Released Under LGPL - original licence link has changed is not relivant.
35414  *
35415  * Fork - LGPL
35416  * <script type="text/javascript">
35417  */
35418  
35419
35420 /**
35421  * @class Roo.menu.DateMenu
35422  * @extends Roo.menu.Menu
35423  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35424  * @constructor
35425  * Creates a new DateMenu
35426  * @param {Object} config Configuration options
35427  */
35428 Roo.menu.DateMenu = function(config){
35429     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35430     this.plain = true;
35431     var di = new Roo.menu.DateItem(config);
35432     this.add(di);
35433     /**
35434      * The {@link Roo.DatePicker} instance for this DateMenu
35435      * @type DatePicker
35436      */
35437     this.picker = di.picker;
35438     /**
35439      * @event select
35440      * @param {DatePicker} picker
35441      * @param {Date} date
35442      */
35443     this.relayEvents(di, ["select"]);
35444
35445     this.on('beforeshow', function(){
35446         if(this.picker){
35447             this.picker.hideMonthPicker(true);
35448         }
35449     }, this);
35450 };
35451 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35452     cls:'x-date-menu'
35453 });/*
35454  * Based on:
35455  * Ext JS Library 1.1.1
35456  * Copyright(c) 2006-2007, Ext JS, LLC.
35457  *
35458  * Originally Released Under LGPL - original licence link has changed is not relivant.
35459  *
35460  * Fork - LGPL
35461  * <script type="text/javascript">
35462  */
35463  
35464
35465 /**
35466  * @class Roo.menu.ColorMenu
35467  * @extends Roo.menu.Menu
35468  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35469  * @constructor
35470  * Creates a new ColorMenu
35471  * @param {Object} config Configuration options
35472  */
35473 Roo.menu.ColorMenu = function(config){
35474     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35475     this.plain = true;
35476     var ci = new Roo.menu.ColorItem(config);
35477     this.add(ci);
35478     /**
35479      * The {@link Roo.ColorPalette} instance for this ColorMenu
35480      * @type ColorPalette
35481      */
35482     this.palette = ci.palette;
35483     /**
35484      * @event select
35485      * @param {ColorPalette} palette
35486      * @param {String} color
35487      */
35488     this.relayEvents(ci, ["select"]);
35489 };
35490 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35491  * Based on:
35492  * Ext JS Library 1.1.1
35493  * Copyright(c) 2006-2007, Ext JS, LLC.
35494  *
35495  * Originally Released Under LGPL - original licence link has changed is not relivant.
35496  *
35497  * Fork - LGPL
35498  * <script type="text/javascript">
35499  */
35500  
35501 /**
35502  * @class Roo.form.Field
35503  * @extends Roo.BoxComponent
35504  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35505  * @constructor
35506  * Creates a new Field
35507  * @param {Object} config Configuration options
35508  */
35509 Roo.form.Field = function(config){
35510     Roo.form.Field.superclass.constructor.call(this, config);
35511 };
35512
35513 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35514     /**
35515      * @cfg {String} fieldLabel Label to use when rendering a form.
35516      */
35517        /**
35518      * @cfg {String} qtip Mouse over tip
35519      */
35520      
35521     /**
35522      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35523      */
35524     invalidClass : "x-form-invalid",
35525     /**
35526      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
35527      */
35528     invalidText : "The value in this field is invalid",
35529     /**
35530      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35531      */
35532     focusClass : "x-form-focus",
35533     /**
35534      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35535       automatic validation (defaults to "keyup").
35536      */
35537     validationEvent : "keyup",
35538     /**
35539      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35540      */
35541     validateOnBlur : true,
35542     /**
35543      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35544      */
35545     validationDelay : 250,
35546     /**
35547      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35548      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35549      */
35550     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35551     /**
35552      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35553      */
35554     fieldClass : "x-form-field",
35555     /**
35556      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35557      *<pre>
35558 Value         Description
35559 -----------   ----------------------------------------------------------------------
35560 qtip          Display a quick tip when the user hovers over the field
35561 title         Display a default browser title attribute popup
35562 under         Add a block div beneath the field containing the error text
35563 side          Add an error icon to the right of the field with a popup on hover
35564 [element id]  Add the error text directly to the innerHTML of the specified element
35565 </pre>
35566      */
35567     msgTarget : 'qtip',
35568     /**
35569      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35570      */
35571     msgFx : 'normal',
35572
35573     /**
35574      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
35575      */
35576     readOnly : false,
35577
35578     /**
35579      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35580      */
35581     disabled : false,
35582
35583     /**
35584      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35585      */
35586     inputType : undefined,
35587     
35588     /**
35589      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
35590          */
35591         tabIndex : undefined,
35592         
35593     // private
35594     isFormField : true,
35595
35596     // private
35597     hasFocus : false,
35598     /**
35599      * @property {Roo.Element} fieldEl
35600      * Element Containing the rendered Field (with label etc.)
35601      */
35602     /**
35603      * @cfg {Mixed} value A value to initialize this field with.
35604      */
35605     value : undefined,
35606
35607     /**
35608      * @cfg {String} name The field's HTML name attribute.
35609      */
35610     /**
35611      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35612      */
35613
35614         // private ??
35615         initComponent : function(){
35616         Roo.form.Field.superclass.initComponent.call(this);
35617         this.addEvents({
35618             /**
35619              * @event focus
35620              * Fires when this field receives input focus.
35621              * @param {Roo.form.Field} this
35622              */
35623             focus : true,
35624             /**
35625              * @event blur
35626              * Fires when this field loses input focus.
35627              * @param {Roo.form.Field} this
35628              */
35629             blur : true,
35630             /**
35631              * @event specialkey
35632              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35633              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35634              * @param {Roo.form.Field} this
35635              * @param {Roo.EventObject} e The event object
35636              */
35637             specialkey : true,
35638             /**
35639              * @event change
35640              * Fires just before the field blurs if the field value has changed.
35641              * @param {Roo.form.Field} this
35642              * @param {Mixed} newValue The new value
35643              * @param {Mixed} oldValue The original value
35644              */
35645             change : true,
35646             /**
35647              * @event invalid
35648              * Fires after the field has been marked as invalid.
35649              * @param {Roo.form.Field} this
35650              * @param {String} msg The validation message
35651              */
35652             invalid : true,
35653             /**
35654              * @event valid
35655              * Fires after the field has been validated with no errors.
35656              * @param {Roo.form.Field} this
35657              */
35658             valid : true,
35659              /**
35660              * @event keyup
35661              * Fires after the key up
35662              * @param {Roo.form.Field} this
35663              * @param {Roo.EventObject}  e The event Object
35664              */
35665             keyup : true
35666         });
35667     },
35668
35669     /**
35670      * Returns the name attribute of the field if available
35671      * @return {String} name The field name
35672      */
35673     getName: function(){
35674          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35675     },
35676
35677     // private
35678     onRender : function(ct, position){
35679         Roo.form.Field.superclass.onRender.call(this, ct, position);
35680         if(!this.el){
35681             var cfg = this.getAutoCreate();
35682             if(!cfg.name){
35683                 cfg.name = this.name || this.id;
35684             }
35685             if(this.inputType){
35686                 cfg.type = this.inputType;
35687             }
35688             this.el = ct.createChild(cfg, position);
35689         }
35690         var type = this.el.dom.type;
35691         if(type){
35692             if(type == 'password'){
35693                 type = 'text';
35694             }
35695             this.el.addClass('x-form-'+type);
35696         }
35697         if(this.readOnly){
35698             this.el.dom.readOnly = true;
35699         }
35700         if(this.tabIndex !== undefined){
35701             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35702         }
35703
35704         this.el.addClass([this.fieldClass, this.cls]);
35705         this.initValue();
35706     },
35707
35708     /**
35709      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35710      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35711      * @return {Roo.form.Field} this
35712      */
35713     applyTo : function(target){
35714         this.allowDomMove = false;
35715         this.el = Roo.get(target);
35716         this.render(this.el.dom.parentNode);
35717         return this;
35718     },
35719
35720     // private
35721     initValue : function(){
35722         if(this.value !== undefined){
35723             this.setValue(this.value);
35724         }else if(this.el.dom.value.length > 0){
35725             this.setValue(this.el.dom.value);
35726         }
35727     },
35728
35729     /**
35730      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35731      */
35732     isDirty : function() {
35733         if(this.disabled) {
35734             return false;
35735         }
35736         return String(this.getValue()) !== String(this.originalValue);
35737     },
35738
35739     // private
35740     afterRender : function(){
35741         Roo.form.Field.superclass.afterRender.call(this);
35742         this.initEvents();
35743     },
35744
35745     // private
35746     fireKey : function(e){
35747         //Roo.log('field ' + e.getKey());
35748         if(e.isNavKeyPress()){
35749             this.fireEvent("specialkey", this, e);
35750         }
35751     },
35752
35753     /**
35754      * Resets the current field value to the originally loaded value and clears any validation messages
35755      */
35756     reset : function(){
35757         this.setValue(this.originalValue);
35758         this.clearInvalid();
35759     },
35760
35761     // private
35762     initEvents : function(){
35763         // safari killled keypress - so keydown is now used..
35764         this.el.on("keydown" , this.fireKey,  this);
35765         this.el.on("focus", this.onFocus,  this);
35766         this.el.on("blur", this.onBlur,  this);
35767         this.el.relayEvent('keyup', this);
35768
35769         // reference to original value for reset
35770         this.originalValue = this.getValue();
35771     },
35772
35773     // private
35774     onFocus : function(){
35775         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35776             this.el.addClass(this.focusClass);
35777         }
35778         if(!this.hasFocus){
35779             this.hasFocus = true;
35780             this.startValue = this.getValue();
35781             this.fireEvent("focus", this);
35782         }
35783     },
35784
35785     beforeBlur : Roo.emptyFn,
35786
35787     // private
35788     onBlur : function(){
35789         this.beforeBlur();
35790         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35791             this.el.removeClass(this.focusClass);
35792         }
35793         this.hasFocus = false;
35794         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35795             this.validate();
35796         }
35797         var v = this.getValue();
35798         if(String(v) !== String(this.startValue)){
35799             this.fireEvent('change', this, v, this.startValue);
35800         }
35801         this.fireEvent("blur", this);
35802     },
35803
35804     /**
35805      * Returns whether or not the field value is currently valid
35806      * @param {Boolean} preventMark True to disable marking the field invalid
35807      * @return {Boolean} True if the value is valid, else false
35808      */
35809     isValid : function(preventMark){
35810         if(this.disabled){
35811             return true;
35812         }
35813         var restore = this.preventMark;
35814         this.preventMark = preventMark === true;
35815         var v = this.validateValue(this.processValue(this.getRawValue()));
35816         this.preventMark = restore;
35817         return v;
35818     },
35819
35820     /**
35821      * Validates the field value
35822      * @return {Boolean} True if the value is valid, else false
35823      */
35824     validate : function(){
35825         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35826             this.clearInvalid();
35827             return true;
35828         }
35829         return false;
35830     },
35831
35832     processValue : function(value){
35833         return value;
35834     },
35835
35836     // private
35837     // Subclasses should provide the validation implementation by overriding this
35838     validateValue : function(value){
35839         return true;
35840     },
35841
35842     /**
35843      * Mark this field as invalid
35844      * @param {String} msg The validation message
35845      */
35846     markInvalid : function(msg){
35847         if(!this.rendered || this.preventMark){ // not rendered
35848             return;
35849         }
35850         this.el.addClass(this.invalidClass);
35851         msg = msg || this.invalidText;
35852         switch(this.msgTarget){
35853             case 'qtip':
35854                 this.el.dom.qtip = msg;
35855                 this.el.dom.qclass = 'x-form-invalid-tip';
35856                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35857                     Roo.QuickTips.enable();
35858                 }
35859                 break;
35860             case 'title':
35861                 this.el.dom.title = msg;
35862                 break;
35863             case 'under':
35864                 if(!this.errorEl){
35865                     var elp = this.el.findParent('.x-form-element', 5, true);
35866                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35867                     this.errorEl.setWidth(elp.getWidth(true)-20);
35868                 }
35869                 this.errorEl.update(msg);
35870                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35871                 break;
35872             case 'side':
35873                 if(!this.errorIcon){
35874                     var elp = this.el.findParent('.x-form-element', 5, true);
35875                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35876                 }
35877                 this.alignErrorIcon();
35878                 this.errorIcon.dom.qtip = msg;
35879                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35880                 this.errorIcon.show();
35881                 this.on('resize', this.alignErrorIcon, this);
35882                 break;
35883             default:
35884                 var t = Roo.getDom(this.msgTarget);
35885                 t.innerHTML = msg;
35886                 t.style.display = this.msgDisplay;
35887                 break;
35888         }
35889         this.fireEvent('invalid', this, msg);
35890     },
35891
35892     // private
35893     alignErrorIcon : function(){
35894         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35895     },
35896
35897     /**
35898      * Clear any invalid styles/messages for this field
35899      */
35900     clearInvalid : function(){
35901         if(!this.rendered || this.preventMark){ // not rendered
35902             return;
35903         }
35904         this.el.removeClass(this.invalidClass);
35905         switch(this.msgTarget){
35906             case 'qtip':
35907                 this.el.dom.qtip = '';
35908                 break;
35909             case 'title':
35910                 this.el.dom.title = '';
35911                 break;
35912             case 'under':
35913                 if(this.errorEl){
35914                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35915                 }
35916                 break;
35917             case 'side':
35918                 if(this.errorIcon){
35919                     this.errorIcon.dom.qtip = '';
35920                     this.errorIcon.hide();
35921                     this.un('resize', this.alignErrorIcon, this);
35922                 }
35923                 break;
35924             default:
35925                 var t = Roo.getDom(this.msgTarget);
35926                 t.innerHTML = '';
35927                 t.style.display = 'none';
35928                 break;
35929         }
35930         this.fireEvent('valid', this);
35931     },
35932
35933     /**
35934      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35935      * @return {Mixed} value The field value
35936      */
35937     getRawValue : function(){
35938         var v = this.el.getValue();
35939         if(v === this.emptyText){
35940             v = '';
35941         }
35942         return v;
35943     },
35944
35945     /**
35946      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35947      * @return {Mixed} value The field value
35948      */
35949     getValue : function(){
35950         var v = this.el.getValue();
35951         if(v === this.emptyText || v === undefined){
35952             v = '';
35953         }
35954         return v;
35955     },
35956
35957     /**
35958      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35959      * @param {Mixed} value The value to set
35960      */
35961     setRawValue : function(v){
35962         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35963     },
35964
35965     /**
35966      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35967      * @param {Mixed} value The value to set
35968      */
35969     setValue : function(v){
35970         this.value = v;
35971         if(this.rendered){
35972             this.el.dom.value = (v === null || v === undefined ? '' : v);
35973              this.validate();
35974         }
35975     },
35976
35977     adjustSize : function(w, h){
35978         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35979         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35980         return s;
35981     },
35982
35983     adjustWidth : function(tag, w){
35984         tag = tag.toLowerCase();
35985         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35986             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35987                 if(tag == 'input'){
35988                     return w + 2;
35989                 }
35990                 if(tag = 'textarea'){
35991                     return w-2;
35992                 }
35993             }else if(Roo.isOpera){
35994                 if(tag == 'input'){
35995                     return w + 2;
35996                 }
35997                 if(tag = 'textarea'){
35998                     return w-2;
35999                 }
36000             }
36001         }
36002         return w;
36003     }
36004 });
36005
36006
36007 // anything other than normal should be considered experimental
36008 Roo.form.Field.msgFx = {
36009     normal : {
36010         show: function(msgEl, f){
36011             msgEl.setDisplayed('block');
36012         },
36013
36014         hide : function(msgEl, f){
36015             msgEl.setDisplayed(false).update('');
36016         }
36017     },
36018
36019     slide : {
36020         show: function(msgEl, f){
36021             msgEl.slideIn('t', {stopFx:true});
36022         },
36023
36024         hide : function(msgEl, f){
36025             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36026         }
36027     },
36028
36029     slideRight : {
36030         show: function(msgEl, f){
36031             msgEl.fixDisplay();
36032             msgEl.alignTo(f.el, 'tl-tr');
36033             msgEl.slideIn('l', {stopFx:true});
36034         },
36035
36036         hide : function(msgEl, f){
36037             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36038         }
36039     }
36040 };/*
36041  * Based on:
36042  * Ext JS Library 1.1.1
36043  * Copyright(c) 2006-2007, Ext JS, LLC.
36044  *
36045  * Originally Released Under LGPL - original licence link has changed is not relivant.
36046  *
36047  * Fork - LGPL
36048  * <script type="text/javascript">
36049  */
36050  
36051
36052 /**
36053  * @class Roo.form.TextField
36054  * @extends Roo.form.Field
36055  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36056  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36057  * @constructor
36058  * Creates a new TextField
36059  * @param {Object} config Configuration options
36060  */
36061 Roo.form.TextField = function(config){
36062     Roo.form.TextField.superclass.constructor.call(this, config);
36063     this.addEvents({
36064         /**
36065          * @event autosize
36066          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36067          * according to the default logic, but this event provides a hook for the developer to apply additional
36068          * logic at runtime to resize the field if needed.
36069              * @param {Roo.form.Field} this This text field
36070              * @param {Number} width The new field width
36071              */
36072         autosize : true
36073     });
36074 };
36075
36076 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36077     /**
36078      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36079      */
36080     grow : false,
36081     /**
36082      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36083      */
36084     growMin : 30,
36085     /**
36086      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36087      */
36088     growMax : 800,
36089     /**
36090      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36091      */
36092     vtype : null,
36093     /**
36094      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36095      */
36096     maskRe : null,
36097     /**
36098      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36099      */
36100     disableKeyFilter : false,
36101     /**
36102      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36103      */
36104     allowBlank : true,
36105     /**
36106      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36107      */
36108     minLength : 0,
36109     /**
36110      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36111      */
36112     maxLength : Number.MAX_VALUE,
36113     /**
36114      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36115      */
36116     minLengthText : "The minimum length for this field is {0}",
36117     /**
36118      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36119      */
36120     maxLengthText : "The maximum length for this field is {0}",
36121     /**
36122      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36123      */
36124     selectOnFocus : false,
36125     /**
36126      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36127      */
36128     blankText : "This field is required",
36129     /**
36130      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36131      * If available, this function will be called only after the basic validators all return true, and will be passed the
36132      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36133      */
36134     validator : null,
36135     /**
36136      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36137      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36138      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36139      */
36140     regex : null,
36141     /**
36142      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36143      */
36144     regexText : "",
36145     /**
36146      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36147      */
36148     emptyText : null,
36149     /**
36150      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36151      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36152      */
36153     emptyClass : 'x-form-empty-field',
36154
36155     // private
36156     initEvents : function(){
36157         Roo.form.TextField.superclass.initEvents.call(this);
36158         if(this.validationEvent == 'keyup'){
36159             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36160             this.el.on('keyup', this.filterValidation, this);
36161         }
36162         else if(this.validationEvent !== false){
36163             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36164         }
36165         if(this.selectOnFocus || this.emptyText){
36166             this.on("focus", this.preFocus, this);
36167             if(this.emptyText){
36168                 this.on('blur', this.postBlur, this);
36169                 this.applyEmptyText();
36170             }
36171         }
36172         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36173             this.el.on("keypress", this.filterKeys, this);
36174         }
36175         if(this.grow){
36176             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36177             this.el.on("click", this.autoSize,  this);
36178         }
36179     },
36180
36181     processValue : function(value){
36182         if(this.stripCharsRe){
36183             var newValue = value.replace(this.stripCharsRe, '');
36184             if(newValue !== value){
36185                 this.setRawValue(newValue);
36186                 return newValue;
36187             }
36188         }
36189         return value;
36190     },
36191
36192     filterValidation : function(e){
36193         if(!e.isNavKeyPress()){
36194             this.validationTask.delay(this.validationDelay);
36195         }
36196     },
36197
36198     // private
36199     onKeyUp : function(e){
36200         if(!e.isNavKeyPress()){
36201             this.autoSize();
36202         }
36203     },
36204
36205     /**
36206      * Resets the current field value to the originally-loaded value and clears any validation messages.
36207      * Also adds emptyText and emptyClass if the original value was blank.
36208      */
36209     reset : function(){
36210         Roo.form.TextField.superclass.reset.call(this);
36211         this.applyEmptyText();
36212     },
36213
36214     applyEmptyText : function(){
36215         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36216             this.setRawValue(this.emptyText);
36217             this.el.addClass(this.emptyClass);
36218         }
36219     },
36220
36221     // private
36222     preFocus : function(){
36223         if(this.emptyText){
36224             if(this.el.dom.value == this.emptyText){
36225                 this.setRawValue('');
36226             }
36227             this.el.removeClass(this.emptyClass);
36228         }
36229         if(this.selectOnFocus){
36230             this.el.dom.select();
36231         }
36232     },
36233
36234     // private
36235     postBlur : function(){
36236         this.applyEmptyText();
36237     },
36238
36239     // private
36240     filterKeys : function(e){
36241         var k = e.getKey();
36242         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36243             return;
36244         }
36245         var c = e.getCharCode(), cc = String.fromCharCode(c);
36246         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36247             return;
36248         }
36249         if(!this.maskRe.test(cc)){
36250             e.stopEvent();
36251         }
36252     },
36253
36254     setValue : function(v){
36255         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36256             this.el.removeClass(this.emptyClass);
36257         }
36258         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36259         this.applyEmptyText();
36260         this.autoSize();
36261     },
36262
36263     /**
36264      * Validates a value according to the field's validation rules and marks the field as invalid
36265      * if the validation fails
36266      * @param {Mixed} value The value to validate
36267      * @return {Boolean} True if the value is valid, else false
36268      */
36269     validateValue : function(value){
36270         if(value.length < 1 || value === this.emptyText){ // if it's blank
36271              if(this.allowBlank){
36272                 this.clearInvalid();
36273                 return true;
36274              }else{
36275                 this.markInvalid(this.blankText);
36276                 return false;
36277              }
36278         }
36279         if(value.length < this.minLength){
36280             this.markInvalid(String.format(this.minLengthText, this.minLength));
36281             return false;
36282         }
36283         if(value.length > this.maxLength){
36284             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36285             return false;
36286         }
36287         if(this.vtype){
36288             var vt = Roo.form.VTypes;
36289             if(!vt[this.vtype](value, this)){
36290                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36291                 return false;
36292             }
36293         }
36294         if(typeof this.validator == "function"){
36295             var msg = this.validator(value);
36296             if(msg !== true){
36297                 this.markInvalid(msg);
36298                 return false;
36299             }
36300         }
36301         if(this.regex && !this.regex.test(value)){
36302             this.markInvalid(this.regexText);
36303             return false;
36304         }
36305         return true;
36306     },
36307
36308     /**
36309      * Selects text in this field
36310      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36311      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36312      */
36313     selectText : function(start, end){
36314         var v = this.getRawValue();
36315         if(v.length > 0){
36316             start = start === undefined ? 0 : start;
36317             end = end === undefined ? v.length : end;
36318             var d = this.el.dom;
36319             if(d.setSelectionRange){
36320                 d.setSelectionRange(start, end);
36321             }else if(d.createTextRange){
36322                 var range = d.createTextRange();
36323                 range.moveStart("character", start);
36324                 range.moveEnd("character", v.length-end);
36325                 range.select();
36326             }
36327         }
36328     },
36329
36330     /**
36331      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36332      * This only takes effect if grow = true, and fires the autosize event.
36333      */
36334     autoSize : function(){
36335         if(!this.grow || !this.rendered){
36336             return;
36337         }
36338         if(!this.metrics){
36339             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36340         }
36341         var el = this.el;
36342         var v = el.dom.value;
36343         var d = document.createElement('div');
36344         d.appendChild(document.createTextNode(v));
36345         v = d.innerHTML;
36346         d = null;
36347         v += "&#160;";
36348         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36349         this.el.setWidth(w);
36350         this.fireEvent("autosize", this, w);
36351     }
36352 });/*
36353  * Based on:
36354  * Ext JS Library 1.1.1
36355  * Copyright(c) 2006-2007, Ext JS, LLC.
36356  *
36357  * Originally Released Under LGPL - original licence link has changed is not relivant.
36358  *
36359  * Fork - LGPL
36360  * <script type="text/javascript">
36361  */
36362  
36363 /**
36364  * @class Roo.form.Hidden
36365  * @extends Roo.form.TextField
36366  * Simple Hidden element used on forms 
36367  * 
36368  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36369  * 
36370  * @constructor
36371  * Creates a new Hidden form element.
36372  * @param {Object} config Configuration options
36373  */
36374
36375
36376
36377 // easy hidden field...
36378 Roo.form.Hidden = function(config){
36379     Roo.form.Hidden.superclass.constructor.call(this, config);
36380 };
36381   
36382 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36383     fieldLabel:      '',
36384     inputType:      'hidden',
36385     width:          50,
36386     allowBlank:     true,
36387     labelSeparator: '',
36388     hidden:         true,
36389     itemCls :       'x-form-item-display-none'
36390
36391
36392 });
36393
36394
36395 /*
36396  * Based on:
36397  * Ext JS Library 1.1.1
36398  * Copyright(c) 2006-2007, Ext JS, LLC.
36399  *
36400  * Originally Released Under LGPL - original licence link has changed is not relivant.
36401  *
36402  * Fork - LGPL
36403  * <script type="text/javascript">
36404  */
36405  
36406 /**
36407  * @class Roo.form.TriggerField
36408  * @extends Roo.form.TextField
36409  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36410  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36411  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36412  * for which you can provide a custom implementation.  For example:
36413  * <pre><code>
36414 var trigger = new Roo.form.TriggerField();
36415 trigger.onTriggerClick = myTriggerFn;
36416 trigger.applyTo('my-field');
36417 </code></pre>
36418  *
36419  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36420  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36421  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36422  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36423  * @constructor
36424  * Create a new TriggerField.
36425  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36426  * to the base TextField)
36427  */
36428 Roo.form.TriggerField = function(config){
36429     this.mimicing = false;
36430     Roo.form.TriggerField.superclass.constructor.call(this, config);
36431 };
36432
36433 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36434     /**
36435      * @cfg {String} triggerClass A CSS class to apply to the trigger
36436      */
36437     /**
36438      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36439      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36440      */
36441     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36442     /**
36443      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36444      */
36445     hideTrigger:false,
36446
36447     /** @cfg {Boolean} grow @hide */
36448     /** @cfg {Number} growMin @hide */
36449     /** @cfg {Number} growMax @hide */
36450
36451     /**
36452      * @hide 
36453      * @method
36454      */
36455     autoSize: Roo.emptyFn,
36456     // private
36457     monitorTab : true,
36458     // private
36459     deferHeight : true,
36460
36461     
36462     actionMode : 'wrap',
36463     // private
36464     onResize : function(w, h){
36465         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36466         if(typeof w == 'number'){
36467             var x = w - this.trigger.getWidth();
36468             this.el.setWidth(this.adjustWidth('input', x));
36469             this.trigger.setStyle('left', x+'px');
36470         }
36471     },
36472
36473     // private
36474     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36475
36476     // private
36477     getResizeEl : function(){
36478         return this.wrap;
36479     },
36480
36481     // private
36482     getPositionEl : function(){
36483         return this.wrap;
36484     },
36485
36486     // private
36487     alignErrorIcon : function(){
36488         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36489     },
36490
36491     // private
36492     onRender : function(ct, position){
36493         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36494         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36495         this.trigger = this.wrap.createChild(this.triggerConfig ||
36496                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36497         if(this.hideTrigger){
36498             this.trigger.setDisplayed(false);
36499         }
36500         this.initTrigger();
36501         if(!this.width){
36502             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36503         }
36504     },
36505
36506     // private
36507     initTrigger : function(){
36508         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36509         this.trigger.addClassOnOver('x-form-trigger-over');
36510         this.trigger.addClassOnClick('x-form-trigger-click');
36511     },
36512
36513     // private
36514     onDestroy : function(){
36515         if(this.trigger){
36516             this.trigger.removeAllListeners();
36517             this.trigger.remove();
36518         }
36519         if(this.wrap){
36520             this.wrap.remove();
36521         }
36522         Roo.form.TriggerField.superclass.onDestroy.call(this);
36523     },
36524
36525     // private
36526     onFocus : function(){
36527         Roo.form.TriggerField.superclass.onFocus.call(this);
36528         if(!this.mimicing){
36529             this.wrap.addClass('x-trigger-wrap-focus');
36530             this.mimicing = true;
36531             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36532             if(this.monitorTab){
36533                 this.el.on("keydown", this.checkTab, this);
36534             }
36535         }
36536     },
36537
36538     // private
36539     checkTab : function(e){
36540         if(e.getKey() == e.TAB){
36541             this.triggerBlur();
36542         }
36543     },
36544
36545     // private
36546     onBlur : function(){
36547         // do nothing
36548     },
36549
36550     // private
36551     mimicBlur : function(e, t){
36552         if(!this.wrap.contains(t) && this.validateBlur()){
36553             this.triggerBlur();
36554         }
36555     },
36556
36557     // private
36558     triggerBlur : function(){
36559         this.mimicing = false;
36560         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36561         if(this.monitorTab){
36562             this.el.un("keydown", this.checkTab, this);
36563         }
36564         this.wrap.removeClass('x-trigger-wrap-focus');
36565         Roo.form.TriggerField.superclass.onBlur.call(this);
36566     },
36567
36568     // private
36569     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36570     validateBlur : function(e, t){
36571         return true;
36572     },
36573
36574     // private
36575     onDisable : function(){
36576         Roo.form.TriggerField.superclass.onDisable.call(this);
36577         if(this.wrap){
36578             this.wrap.addClass('x-item-disabled');
36579         }
36580     },
36581
36582     // private
36583     onEnable : function(){
36584         Roo.form.TriggerField.superclass.onEnable.call(this);
36585         if(this.wrap){
36586             this.wrap.removeClass('x-item-disabled');
36587         }
36588     },
36589
36590     // private
36591     onShow : function(){
36592         var ae = this.getActionEl();
36593         
36594         if(ae){
36595             ae.dom.style.display = '';
36596             ae.dom.style.visibility = 'visible';
36597         }
36598     },
36599
36600     // private
36601     
36602     onHide : function(){
36603         var ae = this.getActionEl();
36604         ae.dom.style.display = 'none';
36605     },
36606
36607     /**
36608      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36609      * by an implementing function.
36610      * @method
36611      * @param {EventObject} e
36612      */
36613     onTriggerClick : Roo.emptyFn
36614 });
36615
36616 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36617 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36618 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36619 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36620     initComponent : function(){
36621         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36622
36623         this.triggerConfig = {
36624             tag:'span', cls:'x-form-twin-triggers', cn:[
36625             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36626             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36627         ]};
36628     },
36629
36630     getTrigger : function(index){
36631         return this.triggers[index];
36632     },
36633
36634     initTrigger : function(){
36635         var ts = this.trigger.select('.x-form-trigger', true);
36636         this.wrap.setStyle('overflow', 'hidden');
36637         var triggerField = this;
36638         ts.each(function(t, all, index){
36639             t.hide = function(){
36640                 var w = triggerField.wrap.getWidth();
36641                 this.dom.style.display = 'none';
36642                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36643             };
36644             t.show = function(){
36645                 var w = triggerField.wrap.getWidth();
36646                 this.dom.style.display = '';
36647                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36648             };
36649             var triggerIndex = 'Trigger'+(index+1);
36650
36651             if(this['hide'+triggerIndex]){
36652                 t.dom.style.display = 'none';
36653             }
36654             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36655             t.addClassOnOver('x-form-trigger-over');
36656             t.addClassOnClick('x-form-trigger-click');
36657         }, this);
36658         this.triggers = ts.elements;
36659     },
36660
36661     onTrigger1Click : Roo.emptyFn,
36662     onTrigger2Click : Roo.emptyFn
36663 });/*
36664  * Based on:
36665  * Ext JS Library 1.1.1
36666  * Copyright(c) 2006-2007, Ext JS, LLC.
36667  *
36668  * Originally Released Under LGPL - original licence link has changed is not relivant.
36669  *
36670  * Fork - LGPL
36671  * <script type="text/javascript">
36672  */
36673  
36674 /**
36675  * @class Roo.form.TextArea
36676  * @extends Roo.form.TextField
36677  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36678  * support for auto-sizing.
36679  * @constructor
36680  * Creates a new TextArea
36681  * @param {Object} config Configuration options
36682  */
36683 Roo.form.TextArea = function(config){
36684     Roo.form.TextArea.superclass.constructor.call(this, config);
36685     // these are provided exchanges for backwards compat
36686     // minHeight/maxHeight were replaced by growMin/growMax to be
36687     // compatible with TextField growing config values
36688     if(this.minHeight !== undefined){
36689         this.growMin = this.minHeight;
36690     }
36691     if(this.maxHeight !== undefined){
36692         this.growMax = this.maxHeight;
36693     }
36694 };
36695
36696 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36697     /**
36698      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36699      */
36700     growMin : 60,
36701     /**
36702      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36703      */
36704     growMax: 1000,
36705     /**
36706      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36707      * in the field (equivalent to setting overflow: hidden, defaults to false)
36708      */
36709     preventScrollbars: false,
36710     /**
36711      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36712      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36713      */
36714
36715     // private
36716     onRender : function(ct, position){
36717         if(!this.el){
36718             this.defaultAutoCreate = {
36719                 tag: "textarea",
36720                 style:"width:300px;height:60px;",
36721                 autocomplete: "off"
36722             };
36723         }
36724         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36725         if(this.grow){
36726             this.textSizeEl = Roo.DomHelper.append(document.body, {
36727                 tag: "pre", cls: "x-form-grow-sizer"
36728             });
36729             if(this.preventScrollbars){
36730                 this.el.setStyle("overflow", "hidden");
36731             }
36732             this.el.setHeight(this.growMin);
36733         }
36734     },
36735
36736     onDestroy : function(){
36737         if(this.textSizeEl){
36738             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36739         }
36740         Roo.form.TextArea.superclass.onDestroy.call(this);
36741     },
36742
36743     // private
36744     onKeyUp : function(e){
36745         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36746             this.autoSize();
36747         }
36748     },
36749
36750     /**
36751      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36752      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36753      */
36754     autoSize : function(){
36755         if(!this.grow || !this.textSizeEl){
36756             return;
36757         }
36758         var el = this.el;
36759         var v = el.dom.value;
36760         var ts = this.textSizeEl;
36761
36762         ts.innerHTML = '';
36763         ts.appendChild(document.createTextNode(v));
36764         v = ts.innerHTML;
36765
36766         Roo.fly(ts).setWidth(this.el.getWidth());
36767         if(v.length < 1){
36768             v = "&#160;&#160;";
36769         }else{
36770             if(Roo.isIE){
36771                 v = v.replace(/\n/g, '<p>&#160;</p>');
36772             }
36773             v += "&#160;\n&#160;";
36774         }
36775         ts.innerHTML = v;
36776         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36777         if(h != this.lastHeight){
36778             this.lastHeight = h;
36779             this.el.setHeight(h);
36780             this.fireEvent("autosize", this, h);
36781         }
36782     }
36783 });/*
36784  * Based on:
36785  * Ext JS Library 1.1.1
36786  * Copyright(c) 2006-2007, Ext JS, LLC.
36787  *
36788  * Originally Released Under LGPL - original licence link has changed is not relivant.
36789  *
36790  * Fork - LGPL
36791  * <script type="text/javascript">
36792  */
36793  
36794
36795 /**
36796  * @class Roo.form.NumberField
36797  * @extends Roo.form.TextField
36798  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36799  * @constructor
36800  * Creates a new NumberField
36801  * @param {Object} config Configuration options
36802  */
36803 Roo.form.NumberField = function(config){
36804     Roo.form.NumberField.superclass.constructor.call(this, config);
36805 };
36806
36807 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36808     /**
36809      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36810      */
36811     fieldClass: "x-form-field x-form-num-field",
36812     /**
36813      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36814      */
36815     allowDecimals : true,
36816     /**
36817      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36818      */
36819     decimalSeparator : ".",
36820     /**
36821      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36822      */
36823     decimalPrecision : 2,
36824     /**
36825      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36826      */
36827     allowNegative : true,
36828     /**
36829      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36830      */
36831     minValue : Number.NEGATIVE_INFINITY,
36832     /**
36833      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36834      */
36835     maxValue : Number.MAX_VALUE,
36836     /**
36837      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36838      */
36839     minText : "The minimum value for this field is {0}",
36840     /**
36841      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36842      */
36843     maxText : "The maximum value for this field is {0}",
36844     /**
36845      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36846      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36847      */
36848     nanText : "{0} is not a valid number",
36849
36850     // private
36851     initEvents : function(){
36852         Roo.form.NumberField.superclass.initEvents.call(this);
36853         var allowed = "0123456789";
36854         if(this.allowDecimals){
36855             allowed += this.decimalSeparator;
36856         }
36857         if(this.allowNegative){
36858             allowed += "-";
36859         }
36860         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36861         var keyPress = function(e){
36862             var k = e.getKey();
36863             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36864                 return;
36865             }
36866             var c = e.getCharCode();
36867             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36868                 e.stopEvent();
36869             }
36870         };
36871         this.el.on("keypress", keyPress, this);
36872     },
36873
36874     // private
36875     validateValue : function(value){
36876         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36877             return false;
36878         }
36879         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36880              return true;
36881         }
36882         var num = this.parseValue(value);
36883         if(isNaN(num)){
36884             this.markInvalid(String.format(this.nanText, value));
36885             return false;
36886         }
36887         if(num < this.minValue){
36888             this.markInvalid(String.format(this.minText, this.minValue));
36889             return false;
36890         }
36891         if(num > this.maxValue){
36892             this.markInvalid(String.format(this.maxText, this.maxValue));
36893             return false;
36894         }
36895         return true;
36896     },
36897
36898     getValue : function(){
36899         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36900     },
36901
36902     // private
36903     parseValue : function(value){
36904         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36905         return isNaN(value) ? '' : value;
36906     },
36907
36908     // private
36909     fixPrecision : function(value){
36910         var nan = isNaN(value);
36911         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36912             return nan ? '' : value;
36913         }
36914         return parseFloat(value).toFixed(this.decimalPrecision);
36915     },
36916
36917     setValue : function(v){
36918         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36919     },
36920
36921     // private
36922     decimalPrecisionFcn : function(v){
36923         return Math.floor(v);
36924     },
36925
36926     beforeBlur : function(){
36927         var v = this.parseValue(this.getRawValue());
36928         if(v){
36929             this.setValue(this.fixPrecision(v));
36930         }
36931     }
36932 });/*
36933  * Based on:
36934  * Ext JS Library 1.1.1
36935  * Copyright(c) 2006-2007, Ext JS, LLC.
36936  *
36937  * Originally Released Under LGPL - original licence link has changed is not relivant.
36938  *
36939  * Fork - LGPL
36940  * <script type="text/javascript">
36941  */
36942  
36943 /**
36944  * @class Roo.form.DateField
36945  * @extends Roo.form.TriggerField
36946  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36947 * @constructor
36948 * Create a new DateField
36949 * @param {Object} config
36950  */
36951 Roo.form.DateField = function(config){
36952     Roo.form.DateField.superclass.constructor.call(this, config);
36953     
36954       this.addEvents({
36955          
36956         /**
36957          * @event select
36958          * Fires when a date is selected
36959              * @param {Roo.form.DateField} combo This combo box
36960              * @param {Date} date The date selected
36961              */
36962         'select' : true
36963          
36964     });
36965     
36966     
36967     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36968     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36969     this.ddMatch = null;
36970     if(this.disabledDates){
36971         var dd = this.disabledDates;
36972         var re = "(?:";
36973         for(var i = 0; i < dd.length; i++){
36974             re += dd[i];
36975             if(i != dd.length-1) re += "|";
36976         }
36977         this.ddMatch = new RegExp(re + ")");
36978     }
36979 };
36980
36981 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36982     /**
36983      * @cfg {String} format
36984      * The default date format string which can be overriden for localization support.  The format must be
36985      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36986      */
36987     format : "m/d/y",
36988     /**
36989      * @cfg {String} altFormats
36990      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36991      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36992      */
36993     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36994     /**
36995      * @cfg {Array} disabledDays
36996      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36997      */
36998     disabledDays : null,
36999     /**
37000      * @cfg {String} disabledDaysText
37001      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
37002      */
37003     disabledDaysText : "Disabled",
37004     /**
37005      * @cfg {Array} disabledDates
37006      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
37007      * expression so they are very powerful. Some examples:
37008      * <ul>
37009      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
37010      * <li>["03/08", "09/16"] would disable those days for every year</li>
37011      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37012      * <li>["03/../2006"] would disable every day in March 2006</li>
37013      * <li>["^03"] would disable every day in every March</li>
37014      * </ul>
37015      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37016      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37017      */
37018     disabledDates : null,
37019     /**
37020      * @cfg {String} disabledDatesText
37021      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37022      */
37023     disabledDatesText : "Disabled",
37024     /**
37025      * @cfg {Date/String} minValue
37026      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37027      * valid format (defaults to null).
37028      */
37029     minValue : null,
37030     /**
37031      * @cfg {Date/String} maxValue
37032      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37033      * valid format (defaults to null).
37034      */
37035     maxValue : null,
37036     /**
37037      * @cfg {String} minText
37038      * The error text to display when the date in the cell is before minValue (defaults to
37039      * 'The date in this field must be after {minValue}').
37040      */
37041     minText : "The date in this field must be equal to or after {0}",
37042     /**
37043      * @cfg {String} maxText
37044      * The error text to display when the date in the cell is after maxValue (defaults to
37045      * 'The date in this field must be before {maxValue}').
37046      */
37047     maxText : "The date in this field must be equal to or before {0}",
37048     /**
37049      * @cfg {String} invalidText
37050      * The error text to display when the date in the field is invalid (defaults to
37051      * '{value} is not a valid date - it must be in the format {format}').
37052      */
37053     invalidText : "{0} is not a valid date - it must be in the format {1}",
37054     /**
37055      * @cfg {String} triggerClass
37056      * An additional CSS class used to style the trigger button.  The trigger will always get the
37057      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37058      * which displays a calendar icon).
37059      */
37060     triggerClass : 'x-form-date-trigger',
37061     
37062
37063     /**
37064      * @cfg {bool} useIso
37065      * if enabled, then the date field will use a hidden field to store the 
37066      * real value as iso formated date. default (false)
37067      */ 
37068     useIso : false,
37069     /**
37070      * @cfg {String/Object} autoCreate
37071      * A DomHelper element spec, or true for a default element spec (defaults to
37072      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37073      */ 
37074     // private
37075     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37076     
37077     // private
37078     hiddenField: false,
37079     
37080     onRender : function(ct, position)
37081     {
37082         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37083         if (this.useIso) {
37084             this.el.dom.removeAttribute('name'); 
37085             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37086                     'before', true);
37087             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
37088             // prevent input submission
37089             this.hiddenName = this.name;
37090         }
37091             
37092             
37093     },
37094     
37095     // private
37096     validateValue : function(value)
37097     {
37098         value = this.formatDate(value);
37099         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37100             return false;
37101         }
37102         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37103              return true;
37104         }
37105         var svalue = value;
37106         value = this.parseDate(value);
37107         if(!value){
37108             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37109             return false;
37110         }
37111         var time = value.getTime();
37112         if(this.minValue && time < this.minValue.getTime()){
37113             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37114             return false;
37115         }
37116         if(this.maxValue && time > this.maxValue.getTime()){
37117             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37118             return false;
37119         }
37120         if(this.disabledDays){
37121             var day = value.getDay();
37122             for(var i = 0; i < this.disabledDays.length; i++) {
37123                 if(day === this.disabledDays[i]){
37124                     this.markInvalid(this.disabledDaysText);
37125                     return false;
37126                 }
37127             }
37128         }
37129         var fvalue = this.formatDate(value);
37130         if(this.ddMatch && this.ddMatch.test(fvalue)){
37131             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37132             return false;
37133         }
37134         return true;
37135     },
37136
37137     // private
37138     // Provides logic to override the default TriggerField.validateBlur which just returns true
37139     validateBlur : function(){
37140         return !this.menu || !this.menu.isVisible();
37141     },
37142
37143     /**
37144      * Returns the current date value of the date field.
37145      * @return {Date} The date value
37146      */
37147     getValue : function(){
37148         
37149         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37150     },
37151
37152     /**
37153      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37154      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37155      * (the default format used is "m/d/y").
37156      * <br />Usage:
37157      * <pre><code>
37158 //All of these calls set the same date value (May 4, 2006)
37159
37160 //Pass a date object:
37161 var dt = new Date('5/4/06');
37162 dateField.setValue(dt);
37163
37164 //Pass a date string (default format):
37165 dateField.setValue('5/4/06');
37166
37167 //Pass a date string (custom format):
37168 dateField.format = 'Y-m-d';
37169 dateField.setValue('2006-5-4');
37170 </code></pre>
37171      * @param {String/Date} date The date or valid date string
37172      */
37173     setValue : function(date){
37174         if (this.hiddenField) {
37175             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37176         }
37177         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37178     },
37179
37180     // private
37181     parseDate : function(value){
37182         if(!value || value instanceof Date){
37183             return value;
37184         }
37185         var v = Date.parseDate(value, this.format);
37186         if(!v && this.altFormats){
37187             if(!this.altFormatsArray){
37188                 this.altFormatsArray = this.altFormats.split("|");
37189             }
37190             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37191                 v = Date.parseDate(value, this.altFormatsArray[i]);
37192             }
37193         }
37194         return v;
37195     },
37196
37197     // private
37198     formatDate : function(date, fmt){
37199         return (!date || !(date instanceof Date)) ?
37200                date : date.dateFormat(fmt || this.format);
37201     },
37202
37203     // private
37204     menuListeners : {
37205         select: function(m, d){
37206             this.setValue(d);
37207             this.fireEvent('select', this, d);
37208         },
37209         show : function(){ // retain focus styling
37210             this.onFocus();
37211         },
37212         hide : function(){
37213             this.focus.defer(10, this);
37214             var ml = this.menuListeners;
37215             this.menu.un("select", ml.select,  this);
37216             this.menu.un("show", ml.show,  this);
37217             this.menu.un("hide", ml.hide,  this);
37218         }
37219     },
37220
37221     // private
37222     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37223     onTriggerClick : function(){
37224         if(this.disabled){
37225             return;
37226         }
37227         if(this.menu == null){
37228             this.menu = new Roo.menu.DateMenu();
37229         }
37230         Roo.apply(this.menu.picker,  {
37231             showClear: this.allowBlank,
37232             minDate : this.minValue,
37233             maxDate : this.maxValue,
37234             disabledDatesRE : this.ddMatch,
37235             disabledDatesText : this.disabledDatesText,
37236             disabledDays : this.disabledDays,
37237             disabledDaysText : this.disabledDaysText,
37238             format : this.format,
37239             minText : String.format(this.minText, this.formatDate(this.minValue)),
37240             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37241         });
37242         this.menu.on(Roo.apply({}, this.menuListeners, {
37243             scope:this
37244         }));
37245         this.menu.picker.setValue(this.getValue() || new Date());
37246         this.menu.show(this.el, "tl-bl?");
37247     },
37248
37249     beforeBlur : function(){
37250         var v = this.parseDate(this.getRawValue());
37251         if(v){
37252             this.setValue(v);
37253         }
37254     }
37255
37256     /** @cfg {Boolean} grow @hide */
37257     /** @cfg {Number} growMin @hide */
37258     /** @cfg {Number} growMax @hide */
37259     /**
37260      * @hide
37261      * @method autoSize
37262      */
37263 });/*
37264  * Based on:
37265  * Ext JS Library 1.1.1
37266  * Copyright(c) 2006-2007, Ext JS, LLC.
37267  *
37268  * Originally Released Under LGPL - original licence link has changed is not relivant.
37269  *
37270  * Fork - LGPL
37271  * <script type="text/javascript">
37272  */
37273  
37274
37275 /**
37276  * @class Roo.form.ComboBox
37277  * @extends Roo.form.TriggerField
37278  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37279  * @constructor
37280  * Create a new ComboBox.
37281  * @param {Object} config Configuration options
37282  */
37283 Roo.form.ComboBox = function(config){
37284     Roo.form.ComboBox.superclass.constructor.call(this, config);
37285     this.addEvents({
37286         /**
37287          * @event expand
37288          * Fires when the dropdown list is expanded
37289              * @param {Roo.form.ComboBox} combo This combo box
37290              */
37291         'expand' : true,
37292         /**
37293          * @event collapse
37294          * Fires when the dropdown list is collapsed
37295              * @param {Roo.form.ComboBox} combo This combo box
37296              */
37297         'collapse' : true,
37298         /**
37299          * @event beforeselect
37300          * Fires before a list item is selected. Return false to cancel the selection.
37301              * @param {Roo.form.ComboBox} combo This combo box
37302              * @param {Roo.data.Record} record The data record returned from the underlying store
37303              * @param {Number} index The index of the selected item in the dropdown list
37304              */
37305         'beforeselect' : true,
37306         /**
37307          * @event select
37308          * Fires when a list item is selected
37309              * @param {Roo.form.ComboBox} combo This combo box
37310              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37311              * @param {Number} index The index of the selected item in the dropdown list
37312              */
37313         'select' : true,
37314         /**
37315          * @event beforequery
37316          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37317          * The event object passed has these properties:
37318              * @param {Roo.form.ComboBox} combo This combo box
37319              * @param {String} query The query
37320              * @param {Boolean} forceAll true to force "all" query
37321              * @param {Boolean} cancel true to cancel the query
37322              * @param {Object} e The query event object
37323              */
37324         'beforequery': true,
37325          /**
37326          * @event add
37327          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37328              * @param {Roo.form.ComboBox} combo This combo box
37329              */
37330         'add' : true,
37331         /**
37332          * @event edit
37333          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37334              * @param {Roo.form.ComboBox} combo This combo box
37335              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37336              */
37337         'edit' : true
37338         
37339         
37340     });
37341     if(this.transform){
37342         this.allowDomMove = false;
37343         var s = Roo.getDom(this.transform);
37344         if(!this.hiddenName){
37345             this.hiddenName = s.name;
37346         }
37347         if(!this.store){
37348             this.mode = 'local';
37349             var d = [], opts = s.options;
37350             for(var i = 0, len = opts.length;i < len; i++){
37351                 var o = opts[i];
37352                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37353                 if(o.selected) {
37354                     this.value = value;
37355                 }
37356                 d.push([value, o.text]);
37357             }
37358             this.store = new Roo.data.SimpleStore({
37359                 'id': 0,
37360                 fields: ['value', 'text'],
37361                 data : d
37362             });
37363             this.valueField = 'value';
37364             this.displayField = 'text';
37365         }
37366         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37367         if(!this.lazyRender){
37368             this.target = true;
37369             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37370             s.parentNode.removeChild(s); // remove it
37371             this.render(this.el.parentNode);
37372         }else{
37373             s.parentNode.removeChild(s); // remove it
37374         }
37375
37376     }
37377     if (this.store) {
37378         this.store = Roo.factory(this.store, Roo.data);
37379     }
37380     
37381     this.selectedIndex = -1;
37382     if(this.mode == 'local'){
37383         if(config.queryDelay === undefined){
37384             this.queryDelay = 10;
37385         }
37386         if(config.minChars === undefined){
37387             this.minChars = 0;
37388         }
37389     }
37390 };
37391
37392 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37393     /**
37394      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37395      */
37396     /**
37397      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37398      * rendering into an Roo.Editor, defaults to false)
37399      */
37400     /**
37401      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37402      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37403      */
37404     /**
37405      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37406      */
37407     /**
37408      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37409      * the dropdown list (defaults to undefined, with no header element)
37410      */
37411
37412      /**
37413      * @cfg {String/Roo.Template} tpl The template to use to render the output
37414      */
37415      
37416     // private
37417     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37418     /**
37419      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37420      */
37421     listWidth: undefined,
37422     /**
37423      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37424      * mode = 'remote' or 'text' if mode = 'local')
37425      */
37426     displayField: undefined,
37427     /**
37428      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37429      * mode = 'remote' or 'value' if mode = 'local'). 
37430      * Note: use of a valueField requires the user make a selection
37431      * in order for a value to be mapped.
37432      */
37433     valueField: undefined,
37434     
37435     
37436     /**
37437      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37438      * field's data value (defaults to the underlying DOM element's name)
37439      */
37440     hiddenName: undefined,
37441     /**
37442      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37443      */
37444     listClass: '',
37445     /**
37446      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37447      */
37448     selectedClass: 'x-combo-selected',
37449     /**
37450      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37451      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37452      * which displays a downward arrow icon).
37453      */
37454     triggerClass : 'x-form-arrow-trigger',
37455     /**
37456      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37457      */
37458     shadow:'sides',
37459     /**
37460      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37461      * anchor positions (defaults to 'tl-bl')
37462      */
37463     listAlign: 'tl-bl?',
37464     /**
37465      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37466      */
37467     maxHeight: 300,
37468     /**
37469      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37470      * query specified by the allQuery config option (defaults to 'query')
37471      */
37472     triggerAction: 'query',
37473     /**
37474      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37475      * (defaults to 4, does not apply if editable = false)
37476      */
37477     minChars : 4,
37478     /**
37479      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37480      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37481      */
37482     typeAhead: false,
37483     /**
37484      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37485      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37486      */
37487     queryDelay: 500,
37488     /**
37489      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37490      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37491      */
37492     pageSize: 0,
37493     /**
37494      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37495      * when editable = true (defaults to false)
37496      */
37497     selectOnFocus:false,
37498     /**
37499      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37500      */
37501     queryParam: 'query',
37502     /**
37503      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37504      * when mode = 'remote' (defaults to 'Loading...')
37505      */
37506     loadingText: 'Loading...',
37507     /**
37508      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37509      */
37510     resizable: false,
37511     /**
37512      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37513      */
37514     handleHeight : 8,
37515     /**
37516      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37517      * traditional select (defaults to true)
37518      */
37519     editable: true,
37520     /**
37521      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37522      */
37523     allQuery: '',
37524     /**
37525      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37526      */
37527     mode: 'remote',
37528     /**
37529      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37530      * listWidth has a higher value)
37531      */
37532     minListWidth : 70,
37533     /**
37534      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37535      * allow the user to set arbitrary text into the field (defaults to false)
37536      */
37537     forceSelection:false,
37538     /**
37539      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37540      * if typeAhead = true (defaults to 250)
37541      */
37542     typeAheadDelay : 250,
37543     /**
37544      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37545      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37546      */
37547     valueNotFoundText : undefined,
37548     /**
37549      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37550      */
37551     blockFocus : false,
37552     
37553     /**
37554      * @cfg {Boolean} disableClear Disable showing of clear button.
37555      */
37556     disableClear : false,
37557     /**
37558      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37559      */
37560     alwaysQuery : false,
37561     
37562     //private
37563     addicon : false,
37564     editicon: false,
37565     
37566     // element that contains real text value.. (when hidden is used..)
37567      
37568     // private
37569     onRender : function(ct, position){
37570         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37571         if(this.hiddenName){
37572             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37573                     'before', true);
37574             this.hiddenField.value =
37575                 this.hiddenValue !== undefined ? this.hiddenValue :
37576                 this.value !== undefined ? this.value : '';
37577
37578             // prevent input submission
37579             this.el.dom.removeAttribute('name');
37580              
37581              
37582         }
37583         if(Roo.isGecko){
37584             this.el.dom.setAttribute('autocomplete', 'off');
37585         }
37586
37587         var cls = 'x-combo-list';
37588
37589         this.list = new Roo.Layer({
37590             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37591         });
37592
37593         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37594         this.list.setWidth(lw);
37595         this.list.swallowEvent('mousewheel');
37596         this.assetHeight = 0;
37597
37598         if(this.title){
37599             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37600             this.assetHeight += this.header.getHeight();
37601         }
37602
37603         this.innerList = this.list.createChild({cls:cls+'-inner'});
37604         this.innerList.on('mouseover', this.onViewOver, this);
37605         this.innerList.on('mousemove', this.onViewMove, this);
37606         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37607         
37608         if(this.allowBlank && !this.pageSize && !this.disableClear){
37609             this.footer = this.list.createChild({cls:cls+'-ft'});
37610             this.pageTb = new Roo.Toolbar(this.footer);
37611            
37612         }
37613         if(this.pageSize){
37614             this.footer = this.list.createChild({cls:cls+'-ft'});
37615             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37616                     {pageSize: this.pageSize});
37617             
37618         }
37619         
37620         if (this.pageTb && this.allowBlank && !this.disableClear) {
37621             var _this = this;
37622             this.pageTb.add(new Roo.Toolbar.Fill(), {
37623                 cls: 'x-btn-icon x-btn-clear',
37624                 text: '&#160;',
37625                 handler: function()
37626                 {
37627                     _this.collapse();
37628                     _this.clearValue();
37629                     _this.onSelect(false, -1);
37630                 }
37631             });
37632         }
37633         if (this.footer) {
37634             this.assetHeight += this.footer.getHeight();
37635         }
37636         
37637
37638         if(!this.tpl){
37639             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37640         }
37641
37642         this.view = new Roo.View(this.innerList, this.tpl, {
37643             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37644         });
37645
37646         this.view.on('click', this.onViewClick, this);
37647
37648         this.store.on('beforeload', this.onBeforeLoad, this);
37649         this.store.on('load', this.onLoad, this);
37650         this.store.on('loadexception', this.collapse, this);
37651
37652         if(this.resizable){
37653             this.resizer = new Roo.Resizable(this.list,  {
37654                pinned:true, handles:'se'
37655             });
37656             this.resizer.on('resize', function(r, w, h){
37657                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37658                 this.listWidth = w;
37659                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37660                 this.restrictHeight();
37661             }, this);
37662             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37663         }
37664         if(!this.editable){
37665             this.editable = true;
37666             this.setEditable(false);
37667         }  
37668         
37669         
37670         if (typeof(this.events.add.listeners) != 'undefined') {
37671             
37672             this.addicon = this.wrap.createChild(
37673                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37674        
37675             this.addicon.on('click', function(e) {
37676                 this.fireEvent('add', this);
37677             }, this);
37678         }
37679         if (typeof(this.events.edit.listeners) != 'undefined') {
37680             
37681             this.editicon = this.wrap.createChild(
37682                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37683             if (this.addicon) {
37684                 this.editicon.setStyle('margin-left', '40px');
37685             }
37686             this.editicon.on('click', function(e) {
37687                 
37688                 // we fire even  if inothing is selected..
37689                 this.fireEvent('edit', this, this.lastData );
37690                 
37691             }, this);
37692         }
37693         
37694         
37695         
37696     },
37697
37698     // private
37699     initEvents : function(){
37700         Roo.form.ComboBox.superclass.initEvents.call(this);
37701
37702         this.keyNav = new Roo.KeyNav(this.el, {
37703             "up" : function(e){
37704                 this.inKeyMode = true;
37705                 this.selectPrev();
37706             },
37707
37708             "down" : function(e){
37709                 if(!this.isExpanded()){
37710                     this.onTriggerClick();
37711                 }else{
37712                     this.inKeyMode = true;
37713                     this.selectNext();
37714                 }
37715             },
37716
37717             "enter" : function(e){
37718                 this.onViewClick();
37719                 //return true;
37720             },
37721
37722             "esc" : function(e){
37723                 this.collapse();
37724             },
37725
37726             "tab" : function(e){
37727                 this.onViewClick(false);
37728                 this.fireEvent("specialkey", this, e);
37729                 return true;
37730             },
37731
37732             scope : this,
37733
37734             doRelay : function(foo, bar, hname){
37735                 if(hname == 'down' || this.scope.isExpanded()){
37736                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37737                 }
37738                 return true;
37739             },
37740
37741             forceKeyDown: true
37742         });
37743         this.queryDelay = Math.max(this.queryDelay || 10,
37744                 this.mode == 'local' ? 10 : 250);
37745         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37746         if(this.typeAhead){
37747             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37748         }
37749         if(this.editable !== false){
37750             this.el.on("keyup", this.onKeyUp, this);
37751         }
37752         if(this.forceSelection){
37753             this.on('blur', this.doForce, this);
37754         }
37755     },
37756
37757     onDestroy : function(){
37758         if(this.view){
37759             this.view.setStore(null);
37760             this.view.el.removeAllListeners();
37761             this.view.el.remove();
37762             this.view.purgeListeners();
37763         }
37764         if(this.list){
37765             this.list.destroy();
37766         }
37767         if(this.store){
37768             this.store.un('beforeload', this.onBeforeLoad, this);
37769             this.store.un('load', this.onLoad, this);
37770             this.store.un('loadexception', this.collapse, this);
37771         }
37772         Roo.form.ComboBox.superclass.onDestroy.call(this);
37773     },
37774
37775     // private
37776     fireKey : function(e){
37777         if(e.isNavKeyPress() && !this.list.isVisible()){
37778             this.fireEvent("specialkey", this, e);
37779         }
37780     },
37781
37782     // private
37783     onResize: function(w, h){
37784         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37785         
37786         if(typeof w != 'number'){
37787             // we do not handle it!?!?
37788             return;
37789         }
37790         var tw = this.trigger.getWidth();
37791         tw += this.addicon ? this.addicon.getWidth() : 0;
37792         tw += this.editicon ? this.editicon.getWidth() : 0;
37793         var x = w - tw;
37794         this.el.setWidth( this.adjustWidth('input', x));
37795             
37796         this.trigger.setStyle('left', x+'px');
37797         
37798         if(this.list && this.listWidth === undefined){
37799             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37800             this.list.setWidth(lw);
37801             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37802         }
37803         
37804     
37805         
37806     },
37807
37808     /**
37809      * Allow or prevent the user from directly editing the field text.  If false is passed,
37810      * the user will only be able to select from the items defined in the dropdown list.  This method
37811      * is the runtime equivalent of setting the 'editable' config option at config time.
37812      * @param {Boolean} value True to allow the user to directly edit the field text
37813      */
37814     setEditable : function(value){
37815         if(value == this.editable){
37816             return;
37817         }
37818         this.editable = value;
37819         if(!value){
37820             this.el.dom.setAttribute('readOnly', true);
37821             this.el.on('mousedown', this.onTriggerClick,  this);
37822             this.el.addClass('x-combo-noedit');
37823         }else{
37824             this.el.dom.setAttribute('readOnly', false);
37825             this.el.un('mousedown', this.onTriggerClick,  this);
37826             this.el.removeClass('x-combo-noedit');
37827         }
37828     },
37829
37830     // private
37831     onBeforeLoad : function(){
37832         if(!this.hasFocus){
37833             return;
37834         }
37835         this.innerList.update(this.loadingText ?
37836                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37837         this.restrictHeight();
37838         this.selectedIndex = -1;
37839     },
37840
37841     // private
37842     onLoad : function(){
37843         if(!this.hasFocus){
37844             return;
37845         }
37846         if(this.store.getCount() > 0){
37847             this.expand();
37848             this.restrictHeight();
37849             if(this.lastQuery == this.allQuery){
37850                 if(this.editable){
37851                     this.el.dom.select();
37852                 }
37853                 if(!this.selectByValue(this.value, true)){
37854                     this.select(0, true);
37855                 }
37856             }else{
37857                 this.selectNext();
37858                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37859                     this.taTask.delay(this.typeAheadDelay);
37860                 }
37861             }
37862         }else{
37863             this.onEmptyResults();
37864         }
37865         //this.el.focus();
37866     },
37867
37868     // private
37869     onTypeAhead : function(){
37870         if(this.store.getCount() > 0){
37871             var r = this.store.getAt(0);
37872             var newValue = r.data[this.displayField];
37873             var len = newValue.length;
37874             var selStart = this.getRawValue().length;
37875             if(selStart != len){
37876                 this.setRawValue(newValue);
37877                 this.selectText(selStart, newValue.length);
37878             }
37879         }
37880     },
37881
37882     // private
37883     onSelect : function(record, index){
37884         if(this.fireEvent('beforeselect', this, record, index) !== false){
37885             this.setFromData(index > -1 ? record.data : false);
37886             this.collapse();
37887             this.fireEvent('select', this, record, index);
37888         }
37889     },
37890
37891     /**
37892      * Returns the currently selected field value or empty string if no value is set.
37893      * @return {String} value The selected value
37894      */
37895     getValue : function(){
37896         if(this.valueField){
37897             return typeof this.value != 'undefined' ? this.value : '';
37898         }else{
37899             return Roo.form.ComboBox.superclass.getValue.call(this);
37900         }
37901     },
37902
37903     /**
37904      * Clears any text/value currently set in the field
37905      */
37906     clearValue : function(){
37907         if(this.hiddenField){
37908             this.hiddenField.value = '';
37909         }
37910         this.value = '';
37911         this.setRawValue('');
37912         this.lastSelectionText = '';
37913         this.applyEmptyText();
37914     },
37915
37916     /**
37917      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37918      * will be displayed in the field.  If the value does not match the data value of an existing item,
37919      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37920      * Otherwise the field will be blank (although the value will still be set).
37921      * @param {String} value The value to match
37922      */
37923     setValue : function(v){
37924         var text = v;
37925         if(this.valueField){
37926             var r = this.findRecord(this.valueField, v);
37927             if(r){
37928                 text = r.data[this.displayField];
37929             }else if(this.valueNotFoundText !== undefined){
37930                 text = this.valueNotFoundText;
37931             }
37932         }
37933         this.lastSelectionText = text;
37934         if(this.hiddenField){
37935             this.hiddenField.value = v;
37936         }
37937         Roo.form.ComboBox.superclass.setValue.call(this, text);
37938         this.value = v;
37939     },
37940     /**
37941      * @property {Object} the last set data for the element
37942      */
37943     
37944     lastData : false,
37945     /**
37946      * Sets the value of the field based on a object which is related to the record format for the store.
37947      * @param {Object} value the value to set as. or false on reset?
37948      */
37949     setFromData : function(o){
37950         var dv = ''; // display value
37951         var vv = ''; // value value..
37952         this.lastData = o;
37953         if (this.displayField) {
37954             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37955         } else {
37956             // this is an error condition!!!
37957             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37958         }
37959         
37960         if(this.valueField){
37961             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37962         }
37963         if(this.hiddenField){
37964             this.hiddenField.value = vv;
37965             
37966             this.lastSelectionText = dv;
37967             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37968             this.value = vv;
37969             return;
37970         }
37971         // no hidden field.. - we store the value in 'value', but still display
37972         // display field!!!!
37973         this.lastSelectionText = dv;
37974         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37975         this.value = vv;
37976         
37977         
37978     },
37979     // private
37980     reset : function(){
37981         // overridden so that last data is reset..
37982         this.setValue(this.originalValue);
37983         this.clearInvalid();
37984         this.lastData = false;
37985     },
37986     // private
37987     findRecord : function(prop, value){
37988         var record;
37989         if(this.store.getCount() > 0){
37990             this.store.each(function(r){
37991                 if(r.data[prop] == value){
37992                     record = r;
37993                     return false;
37994                 }
37995                 return true;
37996             });
37997         }
37998         return record;
37999     },
38000     
38001     getName: function()
38002     {
38003         // returns hidden if it's set..
38004         if (!this.rendered) {return ''};
38005         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38006         
38007     },
38008     // private
38009     onViewMove : function(e, t){
38010         this.inKeyMode = false;
38011     },
38012
38013     // private
38014     onViewOver : function(e, t){
38015         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38016             return;
38017         }
38018         var item = this.view.findItemFromChild(t);
38019         if(item){
38020             var index = this.view.indexOf(item);
38021             this.select(index, false);
38022         }
38023     },
38024
38025     // private
38026     onViewClick : function(doFocus)
38027     {
38028         var index = this.view.getSelectedIndexes()[0];
38029         var r = this.store.getAt(index);
38030         if(r){
38031             this.onSelect(r, index);
38032         }
38033         if(doFocus !== false && !this.blockFocus){
38034             this.el.focus();
38035         }
38036     },
38037
38038     // private
38039     restrictHeight : function(){
38040         this.innerList.dom.style.height = '';
38041         var inner = this.innerList.dom;
38042         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38043         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38044         this.list.beginUpdate();
38045         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38046         this.list.alignTo(this.el, this.listAlign);
38047         this.list.endUpdate();
38048     },
38049
38050     // private
38051     onEmptyResults : function(){
38052         this.collapse();
38053     },
38054
38055     /**
38056      * Returns true if the dropdown list is expanded, else false.
38057      */
38058     isExpanded : function(){
38059         return this.list.isVisible();
38060     },
38061
38062     /**
38063      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38064      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38065      * @param {String} value The data value of the item to select
38066      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38067      * selected item if it is not currently in view (defaults to true)
38068      * @return {Boolean} True if the value matched an item in the list, else false
38069      */
38070     selectByValue : function(v, scrollIntoView){
38071         if(v !== undefined && v !== null){
38072             var r = this.findRecord(this.valueField || this.displayField, v);
38073             if(r){
38074                 this.select(this.store.indexOf(r), scrollIntoView);
38075                 return true;
38076             }
38077         }
38078         return false;
38079     },
38080
38081     /**
38082      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38083      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38084      * @param {Number} index The zero-based index of the list item to select
38085      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38086      * selected item if it is not currently in view (defaults to true)
38087      */
38088     select : function(index, scrollIntoView){
38089         this.selectedIndex = index;
38090         this.view.select(index);
38091         if(scrollIntoView !== false){
38092             var el = this.view.getNode(index);
38093             if(el){
38094                 this.innerList.scrollChildIntoView(el, false);
38095             }
38096         }
38097     },
38098
38099     // private
38100     selectNext : function(){
38101         var ct = this.store.getCount();
38102         if(ct > 0){
38103             if(this.selectedIndex == -1){
38104                 this.select(0);
38105             }else if(this.selectedIndex < ct-1){
38106                 this.select(this.selectedIndex+1);
38107             }
38108         }
38109     },
38110
38111     // private
38112     selectPrev : function(){
38113         var ct = this.store.getCount();
38114         if(ct > 0){
38115             if(this.selectedIndex == -1){
38116                 this.select(0);
38117             }else if(this.selectedIndex != 0){
38118                 this.select(this.selectedIndex-1);
38119             }
38120         }
38121     },
38122
38123     // private
38124     onKeyUp : function(e){
38125         if(this.editable !== false && !e.isSpecialKey()){
38126             this.lastKey = e.getKey();
38127             this.dqTask.delay(this.queryDelay);
38128         }
38129     },
38130
38131     // private
38132     validateBlur : function(){
38133         return !this.list || !this.list.isVisible();   
38134     },
38135
38136     // private
38137     initQuery : function(){
38138         this.doQuery(this.getRawValue());
38139     },
38140
38141     // private
38142     doForce : function(){
38143         if(this.el.dom.value.length > 0){
38144             this.el.dom.value =
38145                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38146             this.applyEmptyText();
38147         }
38148     },
38149
38150     /**
38151      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38152      * query allowing the query action to be canceled if needed.
38153      * @param {String} query The SQL query to execute
38154      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38155      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38156      * saved in the current store (defaults to false)
38157      */
38158     doQuery : function(q, forceAll){
38159         if(q === undefined || q === null){
38160             q = '';
38161         }
38162         var qe = {
38163             query: q,
38164             forceAll: forceAll,
38165             combo: this,
38166             cancel:false
38167         };
38168         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38169             return false;
38170         }
38171         q = qe.query;
38172         forceAll = qe.forceAll;
38173         if(forceAll === true || (q.length >= this.minChars)){
38174             if(this.lastQuery != q || this.alwaysQuery){
38175                 this.lastQuery = q;
38176                 if(this.mode == 'local'){
38177                     this.selectedIndex = -1;
38178                     if(forceAll){
38179                         this.store.clearFilter();
38180                     }else{
38181                         this.store.filter(this.displayField, q);
38182                     }
38183                     this.onLoad();
38184                 }else{
38185                     this.store.baseParams[this.queryParam] = q;
38186                     this.store.load({
38187                         params: this.getParams(q)
38188                     });
38189                     this.expand();
38190                 }
38191             }else{
38192                 this.selectedIndex = -1;
38193                 this.onLoad();   
38194             }
38195         }
38196     },
38197
38198     // private
38199     getParams : function(q){
38200         var p = {};
38201         //p[this.queryParam] = q;
38202         if(this.pageSize){
38203             p.start = 0;
38204             p.limit = this.pageSize;
38205         }
38206         return p;
38207     },
38208
38209     /**
38210      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38211      */
38212     collapse : function(){
38213         if(!this.isExpanded()){
38214             return;
38215         }
38216         this.list.hide();
38217         Roo.get(document).un('mousedown', this.collapseIf, this);
38218         Roo.get(document).un('mousewheel', this.collapseIf, this);
38219         if (!this.editable) {
38220             Roo.get(document).un('keydown', this.listKeyPress, this);
38221         }
38222         this.fireEvent('collapse', this);
38223     },
38224
38225     // private
38226     collapseIf : function(e){
38227         if(!e.within(this.wrap) && !e.within(this.list)){
38228             this.collapse();
38229         }
38230     },
38231
38232     /**
38233      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38234      */
38235     expand : function(){
38236         if(this.isExpanded() || !this.hasFocus){
38237             return;
38238         }
38239         this.list.alignTo(this.el, this.listAlign);
38240         this.list.show();
38241         Roo.get(document).on('mousedown', this.collapseIf, this);
38242         Roo.get(document).on('mousewheel', this.collapseIf, this);
38243         if (!this.editable) {
38244             Roo.get(document).on('keydown', this.listKeyPress, this);
38245         }
38246         
38247         this.fireEvent('expand', this);
38248     },
38249
38250     // private
38251     // Implements the default empty TriggerField.onTriggerClick function
38252     onTriggerClick : function(){
38253         if(this.disabled){
38254             return;
38255         }
38256         if(this.isExpanded()){
38257             this.collapse();
38258             if (!this.blockFocus) {
38259                 this.el.focus();
38260             }
38261             
38262         }else {
38263             this.hasFocus = true;
38264             if(this.triggerAction == 'all') {
38265                 this.doQuery(this.allQuery, true);
38266             } else {
38267                 this.doQuery(this.getRawValue());
38268             }
38269             if (!this.blockFocus) {
38270                 this.el.focus();
38271             }
38272         }
38273     },
38274     listKeyPress : function(e)
38275     {
38276         //Roo.log('listkeypress');
38277         // scroll to first matching element based on key pres..
38278         if (e.isSpecialKey()) {
38279             return false;
38280         }
38281         var k = String.fromCharCode(e.getKey()).toUpperCase();
38282         //Roo.log(k);
38283         var match  = false;
38284         var csel = this.view.getSelectedNodes();
38285         var cselitem = false;
38286         if (csel.length) {
38287             var ix = this.view.indexOf(csel[0]);
38288             cselitem  = this.store.getAt(ix);
38289             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38290                 cselitem = false;
38291             }
38292             
38293         }
38294         
38295         this.store.each(function(v) { 
38296             if (cselitem) {
38297                 // start at existing selection.
38298                 if (cselitem.id == v.id) {
38299                     cselitem = false;
38300                 }
38301                 return;
38302             }
38303                 
38304             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38305                 match = this.store.indexOf(v);
38306                 return false;
38307             }
38308         }, this);
38309         
38310         if (match === false) {
38311             return true; // no more action?
38312         }
38313         // scroll to?
38314         this.view.select(match);
38315         var sn = Roo.get(this.view.getSelectedNodes()[0])
38316         sn.scrollIntoView(sn.dom.parentNode, false);
38317     }
38318
38319     /** 
38320     * @cfg {Boolean} grow 
38321     * @hide 
38322     */
38323     /** 
38324     * @cfg {Number} growMin 
38325     * @hide 
38326     */
38327     /** 
38328     * @cfg {Number} growMax 
38329     * @hide 
38330     */
38331     /**
38332      * @hide
38333      * @method autoSize
38334      */
38335 });/*
38336  * Based on:
38337  * Ext JS Library 1.1.1
38338  * Copyright(c) 2006-2007, Ext JS, LLC.
38339  *
38340  * Originally Released Under LGPL - original licence link has changed is not relivant.
38341  *
38342  * Fork - LGPL
38343  * <script type="text/javascript">
38344  */
38345 /**
38346  * @class Roo.form.Checkbox
38347  * @extends Roo.form.Field
38348  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38349  * @constructor
38350  * Creates a new Checkbox
38351  * @param {Object} config Configuration options
38352  */
38353 Roo.form.Checkbox = function(config){
38354     Roo.form.Checkbox.superclass.constructor.call(this, config);
38355     this.addEvents({
38356         /**
38357          * @event check
38358          * Fires when the checkbox is checked or unchecked.
38359              * @param {Roo.form.Checkbox} this This checkbox
38360              * @param {Boolean} checked The new checked value
38361              */
38362         check : true
38363     });
38364 };
38365
38366 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38367     /**
38368      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38369      */
38370     focusClass : undefined,
38371     /**
38372      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38373      */
38374     fieldClass: "x-form-field",
38375     /**
38376      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38377      */
38378     checked: false,
38379     /**
38380      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38381      * {tag: "input", type: "checkbox", autocomplete: "off"})
38382      */
38383     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38384     /**
38385      * @cfg {String} boxLabel The text that appears beside the checkbox
38386      */
38387     boxLabel : "",
38388     /**
38389      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38390      */  
38391     inputValue : '1',
38392     /**
38393      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38394      */
38395      valueOff: '0', // value when not checked..
38396
38397     actionMode : 'viewEl', 
38398     //
38399     // private
38400     itemCls : 'x-menu-check-item x-form-item',
38401     groupClass : 'x-menu-group-item',
38402     inputType : 'hidden',
38403     
38404     
38405     inSetChecked: false, // check that we are not calling self...
38406     
38407     inputElement: false, // real input element?
38408     basedOn: false, // ????
38409     
38410     isFormField: true, // not sure where this is needed!!!!
38411
38412     onResize : function(){
38413         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38414         if(!this.boxLabel){
38415             this.el.alignTo(this.wrap, 'c-c');
38416         }
38417     },
38418
38419     initEvents : function(){
38420         Roo.form.Checkbox.superclass.initEvents.call(this);
38421         this.el.on("click", this.onClick,  this);
38422         this.el.on("change", this.onClick,  this);
38423     },
38424
38425
38426     getResizeEl : function(){
38427         return this.wrap;
38428     },
38429
38430     getPositionEl : function(){
38431         return this.wrap;
38432     },
38433
38434     // private
38435     onRender : function(ct, position){
38436         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38437         /*
38438         if(this.inputValue !== undefined){
38439             this.el.dom.value = this.inputValue;
38440         }
38441         */
38442         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38443         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38444         var viewEl = this.wrap.createChild({ 
38445             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38446         this.viewEl = viewEl;   
38447         this.wrap.on('click', this.onClick,  this); 
38448         
38449         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38450         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38451         
38452         
38453         
38454         if(this.boxLabel){
38455             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38456         //    viewEl.on('click', this.onClick,  this); 
38457         }
38458         //if(this.checked){
38459             this.setChecked(this.checked);
38460         //}else{
38461             //this.checked = this.el.dom;
38462         //}
38463
38464     },
38465
38466     // private
38467     initValue : Roo.emptyFn,
38468
38469     /**
38470      * Returns the checked state of the checkbox.
38471      * @return {Boolean} True if checked, else false
38472      */
38473     getValue : function(){
38474         if(this.el){
38475             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38476         }
38477         return this.valueOff;
38478         
38479     },
38480
38481         // private
38482     onClick : function(){ 
38483         this.setChecked(!this.checked);
38484
38485         //if(this.el.dom.checked != this.checked){
38486         //    this.setValue(this.el.dom.checked);
38487        // }
38488     },
38489
38490     /**
38491      * Sets the checked state of the checkbox.
38492      * On is always based on a string comparison between inputValue and the param.
38493      * @param {Boolean/String} value - the value to set 
38494      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38495      */
38496     setValue : function(v,suppressEvent){
38497         
38498         
38499         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38500         //if(this.el && this.el.dom){
38501         //    this.el.dom.checked = this.checked;
38502         //    this.el.dom.defaultChecked = this.checked;
38503         //}
38504         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38505         //this.fireEvent("check", this, this.checked);
38506     },
38507     // private..
38508     setChecked : function(state,suppressEvent)
38509     {
38510         if (this.inSetChecked) {
38511             this.checked = state;
38512             return;
38513         }
38514         
38515     
38516         if(this.wrap){
38517             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38518         }
38519         this.checked = state;
38520         if(suppressEvent !== true){
38521             this.fireEvent('check', this, state);
38522         }
38523         this.inSetChecked = true;
38524         this.el.dom.value = state ? this.inputValue : this.valueOff;
38525         this.inSetChecked = false;
38526         
38527     },
38528     // handle setting of hidden value by some other method!!?!?
38529     setFromHidden: function()
38530     {
38531         if(!this.el){
38532             return;
38533         }
38534         //console.log("SET FROM HIDDEN");
38535         //alert('setFrom hidden');
38536         this.setValue(this.el.dom.value);
38537     },
38538     
38539     onDestroy : function()
38540     {
38541         if(this.viewEl){
38542             Roo.get(this.viewEl).remove();
38543         }
38544          
38545         Roo.form.Checkbox.superclass.onDestroy.call(this);
38546     }
38547
38548 });/*
38549  * Based on:
38550  * Ext JS Library 1.1.1
38551  * Copyright(c) 2006-2007, Ext JS, LLC.
38552  *
38553  * Originally Released Under LGPL - original licence link has changed is not relivant.
38554  *
38555  * Fork - LGPL
38556  * <script type="text/javascript">
38557  */
38558  
38559 /**
38560  * @class Roo.form.Radio
38561  * @extends Roo.form.Checkbox
38562  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38563  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38564  * @constructor
38565  * Creates a new Radio
38566  * @param {Object} config Configuration options
38567  */
38568 Roo.form.Radio = function(){
38569     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38570 };
38571 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38572     inputType: 'radio',
38573
38574     /**
38575      * If this radio is part of a group, it will return the selected value
38576      * @return {String}
38577      */
38578     getGroupValue : function(){
38579         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38580     }
38581 });//<script type="text/javascript">
38582
38583 /*
38584  * Ext JS Library 1.1.1
38585  * Copyright(c) 2006-2007, Ext JS, LLC.
38586  * licensing@extjs.com
38587  * 
38588  * http://www.extjs.com/license
38589  */
38590  
38591  /*
38592   * 
38593   * Known bugs:
38594   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38595   * - IE ? - no idea how much works there.
38596   * 
38597   * 
38598   * 
38599   */
38600  
38601
38602 /**
38603  * @class Ext.form.HtmlEditor
38604  * @extends Ext.form.Field
38605  * Provides a lightweight HTML Editor component.
38606  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38607  * 
38608  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38609  * supported by this editor.</b><br/><br/>
38610  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38611  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38612  */
38613 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38614       /**
38615      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38616      */
38617     toolbars : false,
38618     /**
38619      * @cfg {String} createLinkText The default text for the create link prompt
38620      */
38621     createLinkText : 'Please enter the URL for the link:',
38622     /**
38623      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38624      */
38625     defaultLinkValue : 'http:/'+'/',
38626    
38627      /**
38628      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
38629      *                        Roo.resizable.
38630      */
38631     resizable : false,
38632      /**
38633      * @cfg {Number} height (in pixels)
38634      */   
38635     height: 300,
38636    /**
38637      * @cfg {Number} width (in pixels)
38638      */   
38639     width: 500,
38640     
38641     /**
38642      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
38643      * 
38644      */
38645     stylesheets: false,
38646     
38647     // id of frame..
38648     frameId: false,
38649     
38650     // private properties
38651     validationEvent : false,
38652     deferHeight: true,
38653     initialized : false,
38654     activated : false,
38655     sourceEditMode : false,
38656     onFocus : Roo.emptyFn,
38657     iframePad:3,
38658     hideMode:'offsets',
38659     
38660     defaultAutoCreate : { // modified by initCompnoent..
38661         tag: "textarea",
38662         style:"width:500px;height:300px;",
38663         autocomplete: "off"
38664     },
38665
38666     // private
38667     initComponent : function(){
38668         this.addEvents({
38669             /**
38670              * @event initialize
38671              * Fires when the editor is fully initialized (including the iframe)
38672              * @param {HtmlEditor} this
38673              */
38674             initialize: true,
38675             /**
38676              * @event activate
38677              * Fires when the editor is first receives the focus. Any insertion must wait
38678              * until after this event.
38679              * @param {HtmlEditor} this
38680              */
38681             activate: true,
38682              /**
38683              * @event beforesync
38684              * Fires before the textarea is updated with content from the editor iframe. Return false
38685              * to cancel the sync.
38686              * @param {HtmlEditor} this
38687              * @param {String} html
38688              */
38689             beforesync: true,
38690              /**
38691              * @event beforepush
38692              * Fires before the iframe editor is updated with content from the textarea. Return false
38693              * to cancel the push.
38694              * @param {HtmlEditor} this
38695              * @param {String} html
38696              */
38697             beforepush: true,
38698              /**
38699              * @event sync
38700              * Fires when the textarea is updated with content from the editor iframe.
38701              * @param {HtmlEditor} this
38702              * @param {String} html
38703              */
38704             sync: true,
38705              /**
38706              * @event push
38707              * Fires when the iframe editor is updated with content from the textarea.
38708              * @param {HtmlEditor} this
38709              * @param {String} html
38710              */
38711             push: true,
38712              /**
38713              * @event editmodechange
38714              * Fires when the editor switches edit modes
38715              * @param {HtmlEditor} this
38716              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38717              */
38718             editmodechange: true,
38719             /**
38720              * @event editorevent
38721              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38722              * @param {HtmlEditor} this
38723              */
38724             editorevent: true
38725         });
38726         this.defaultAutoCreate =  {
38727             tag: "textarea",
38728             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
38729             autocomplete: "off"
38730         };
38731     },
38732
38733     /**
38734      * Protected method that will not generally be called directly. It
38735      * is called when the editor creates its toolbar. Override this method if you need to
38736      * add custom toolbar buttons.
38737      * @param {HtmlEditor} editor
38738      */
38739     createToolbar : function(editor){
38740         if (!editor.toolbars || !editor.toolbars.length) {
38741             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38742         }
38743         
38744         for (var i =0 ; i < editor.toolbars.length;i++) {
38745             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38746             editor.toolbars[i].init(editor);
38747         }
38748          
38749         
38750     },
38751
38752     /**
38753      * Protected method that will not generally be called directly. It
38754      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38755      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38756      */
38757     getDocMarkup : function(){
38758         // body styles..
38759         var st = '';
38760         if (this.stylesheets === false) {
38761             
38762             Roo.get(document.head).select('style').each(function(node) {
38763                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38764             });
38765             
38766             Roo.get(document.head).select('link').each(function(node) { 
38767                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38768             });
38769             
38770         } else if (!this.stylesheets.length) {
38771                 // simple..
38772                 st = '<style type="text/css">' +
38773                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38774                    '</style>';
38775         } else {
38776             Roo.each(this.stylesheets, function(s) {
38777                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
38778             });
38779             
38780         }
38781         
38782         return '<html><head>' + st  +
38783             //<style type="text/css">' +
38784             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38785             //'</style>' +
38786             ' </head><body></body></html>';
38787     },
38788
38789     // private
38790     onRender : function(ct, position)
38791     {
38792         var _t = this;
38793         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38794         this.el.dom.style.border = '0 none';
38795         this.el.dom.setAttribute('tabIndex', -1);
38796         this.el.addClass('x-hidden');
38797         if(Roo.isIE){ // fix IE 1px bogus margin
38798             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38799         }
38800         this.wrap = this.el.wrap({
38801             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38802         });
38803         
38804         if (this.resizable) {
38805             this.resizeEl = new Roo.Resizable(this.wrap, {
38806                 pinned : true,
38807                 wrap: true,
38808                 dynamic : true,
38809                 minHeight : this.height,
38810                 height: this.height,
38811                 handles : this.resizable,
38812                 width: this.width,
38813                 listeners : {
38814                     resize : function(r, w, h) {
38815                         _t.onResize(w,h); // -something
38816                     }
38817                 }
38818             });
38819             
38820         }
38821
38822         this.frameId = Roo.id();
38823         
38824         this.createToolbar(this);
38825         
38826       
38827         
38828         var iframe = this.wrap.createChild({
38829             tag: 'iframe',
38830             id: this.frameId,
38831             name: this.frameId,
38832             frameBorder : 'no',
38833             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38834         }, this.el
38835         );
38836         
38837        // console.log(iframe);
38838         //this.wrap.dom.appendChild(iframe);
38839
38840         this.iframe = iframe.dom;
38841
38842          this.assignDocWin();
38843         
38844         this.doc.designMode = 'on';
38845        
38846         this.doc.open();
38847         this.doc.write(this.getDocMarkup());
38848         this.doc.close();
38849
38850         
38851         var task = { // must defer to wait for browser to be ready
38852             run : function(){
38853                 //console.log("run task?" + this.doc.readyState);
38854                 this.assignDocWin();
38855                 if(this.doc.body || this.doc.readyState == 'complete'){
38856                     try {
38857                         this.doc.designMode="on";
38858                     } catch (e) {
38859                         return;
38860                     }
38861                     Roo.TaskMgr.stop(task);
38862                     this.initEditor.defer(10, this);
38863                 }
38864             },
38865             interval : 10,
38866             duration:10000,
38867             scope: this
38868         };
38869         Roo.TaskMgr.start(task);
38870
38871         if(!this.width){
38872             this.setSize(this.wrap.getSize());
38873         }
38874         if (this.resizeEl) {
38875             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
38876             // should trigger onReize..
38877         }
38878     },
38879
38880     // private
38881     onResize : function(w, h)
38882     {
38883         //Roo.log('resize: ' +w + ',' + h );
38884         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38885         if(this.el && this.iframe){
38886             if(typeof w == 'number'){
38887                 var aw = w - this.wrap.getFrameWidth('lr');
38888                 this.el.setWidth(this.adjustWidth('textarea', aw));
38889                 this.iframe.style.width = aw + 'px';
38890             }
38891             if(typeof h == 'number'){
38892                 var tbh = 0;
38893                 for (var i =0; i < this.toolbars.length;i++) {
38894                     // fixme - ask toolbars for heights?
38895                     tbh += this.toolbars[i].tb.el.getHeight();
38896                     if (this.toolbars[i].footer) {
38897                         tbh += this.toolbars[i].footer.el.getHeight();
38898                     }
38899                 }
38900                 
38901                 
38902                 
38903                 
38904                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38905                 ah -= 5; // knock a few pixes off for look..
38906                 this.el.setHeight(this.adjustWidth('textarea', ah));
38907                 this.iframe.style.height = ah + 'px';
38908                 if(this.doc){
38909                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38910                 }
38911             }
38912         }
38913     },
38914
38915     /**
38916      * Toggles the editor between standard and source edit mode.
38917      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38918      */
38919     toggleSourceEdit : function(sourceEditMode){
38920         
38921         this.sourceEditMode = sourceEditMode === true;
38922         
38923         if(this.sourceEditMode){
38924           
38925             this.syncValue();
38926             this.iframe.className = 'x-hidden';
38927             this.el.removeClass('x-hidden');
38928             this.el.dom.removeAttribute('tabIndex');
38929             this.el.focus();
38930         }else{
38931              
38932             this.pushValue();
38933             this.iframe.className = '';
38934             this.el.addClass('x-hidden');
38935             this.el.dom.setAttribute('tabIndex', -1);
38936             this.deferFocus();
38937         }
38938         this.setSize(this.wrap.getSize());
38939         this.fireEvent('editmodechange', this, this.sourceEditMode);
38940     },
38941
38942     // private used internally
38943     createLink : function(){
38944         var url = prompt(this.createLinkText, this.defaultLinkValue);
38945         if(url && url != 'http:/'+'/'){
38946             this.relayCmd('createlink', url);
38947         }
38948     },
38949
38950     // private (for BoxComponent)
38951     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38952
38953     // private (for BoxComponent)
38954     getResizeEl : function(){
38955         return this.wrap;
38956     },
38957
38958     // private (for BoxComponent)
38959     getPositionEl : function(){
38960         return this.wrap;
38961     },
38962
38963     // private
38964     initEvents : function(){
38965         this.originalValue = this.getValue();
38966     },
38967
38968     /**
38969      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38970      * @method
38971      */
38972     markInvalid : Roo.emptyFn,
38973     /**
38974      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38975      * @method
38976      */
38977     clearInvalid : Roo.emptyFn,
38978
38979     setValue : function(v){
38980         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38981         this.pushValue();
38982     },
38983
38984     /**
38985      * Protected method that will not generally be called directly. If you need/want
38986      * custom HTML cleanup, this is the method you should override.
38987      * @param {String} html The HTML to be cleaned
38988      * return {String} The cleaned HTML
38989      */
38990     cleanHtml : function(html){
38991         html = String(html);
38992         if(html.length > 5){
38993             if(Roo.isSafari){ // strip safari nonsense
38994                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38995             }
38996         }
38997         if(html == '&nbsp;'){
38998             html = '';
38999         }
39000         return html;
39001     },
39002
39003     /**
39004      * Protected method that will not generally be called directly. Syncs the contents
39005      * of the editor iframe with the textarea.
39006      */
39007     syncValue : function(){
39008         if(this.initialized){
39009             var bd = (this.doc.body || this.doc.documentElement);
39010             this.cleanUpPaste();
39011             var html = bd.innerHTML;
39012             if(Roo.isSafari){
39013                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
39014                 var m = bs.match(/text-align:(.*?);/i);
39015                 if(m && m[1]){
39016                     html = '<div style="'+m[0]+'">' + html + '</div>';
39017                 }
39018             }
39019             html = this.cleanHtml(html);
39020             if(this.fireEvent('beforesync', this, html) !== false){
39021                 this.el.dom.value = html;
39022                 this.fireEvent('sync', this, html);
39023             }
39024         }
39025     },
39026
39027     /**
39028      * Protected method that will not generally be called directly. Pushes the value of the textarea
39029      * into the iframe editor.
39030      */
39031     pushValue : function(){
39032         if(this.initialized){
39033             var v = this.el.dom.value;
39034             if(v.length < 1){
39035                 v = '&#160;';
39036             }
39037             
39038             if(this.fireEvent('beforepush', this, v) !== false){
39039                 var d = (this.doc.body || this.doc.documentElement);
39040                 d.innerHTML = v;
39041                 this.cleanUpPaste();
39042                 this.el.dom.value = d.innerHTML;
39043                 this.fireEvent('push', this, v);
39044             }
39045         }
39046     },
39047
39048     // private
39049     deferFocus : function(){
39050         this.focus.defer(10, this);
39051     },
39052
39053     // doc'ed in Field
39054     focus : function(){
39055         if(this.win && !this.sourceEditMode){
39056             this.win.focus();
39057         }else{
39058             this.el.focus();
39059         }
39060     },
39061     
39062     assignDocWin: function()
39063     {
39064         var iframe = this.iframe;
39065         
39066          if(Roo.isIE){
39067             this.doc = iframe.contentWindow.document;
39068             this.win = iframe.contentWindow;
39069         } else {
39070             if (!Roo.get(this.frameId)) {
39071                 return;
39072             }
39073             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
39074             this.win = Roo.get(this.frameId).dom.contentWindow;
39075         }
39076     },
39077     
39078     // private
39079     initEditor : function(){
39080         //console.log("INIT EDITOR");
39081         this.assignDocWin();
39082         
39083         
39084         
39085         this.doc.designMode="on";
39086         this.doc.open();
39087         this.doc.write(this.getDocMarkup());
39088         this.doc.close();
39089         
39090         var dbody = (this.doc.body || this.doc.documentElement);
39091         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
39092         // this copies styles from the containing element into thsi one..
39093         // not sure why we need all of this..
39094         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
39095         ss['background-attachment'] = 'fixed'; // w3c
39096         dbody.bgProperties = 'fixed'; // ie
39097         Roo.DomHelper.applyStyles(dbody, ss);
39098         Roo.EventManager.on(this.doc, {
39099             //'mousedown': this.onEditorEvent,
39100             'mouseup': this.onEditorEvent,
39101             'dblclick': this.onEditorEvent,
39102             'click': this.onEditorEvent,
39103             'keyup': this.onEditorEvent,
39104             buffer:100,
39105             scope: this
39106         });
39107         if(Roo.isGecko){
39108             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
39109         }
39110         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
39111             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
39112         }
39113         this.initialized = true;
39114
39115         this.fireEvent('initialize', this);
39116         this.pushValue();
39117     },
39118
39119     // private
39120     onDestroy : function(){
39121         
39122         
39123         
39124         if(this.rendered){
39125             
39126             for (var i =0; i < this.toolbars.length;i++) {
39127                 // fixme - ask toolbars for heights?
39128                 this.toolbars[i].onDestroy();
39129             }
39130             
39131             this.wrap.dom.innerHTML = '';
39132             this.wrap.remove();
39133         }
39134     },
39135
39136     // private
39137     onFirstFocus : function(){
39138         
39139         this.assignDocWin();
39140         
39141         
39142         this.activated = true;
39143         for (var i =0; i < this.toolbars.length;i++) {
39144             this.toolbars[i].onFirstFocus();
39145         }
39146        
39147         if(Roo.isGecko){ // prevent silly gecko errors
39148             this.win.focus();
39149             var s = this.win.getSelection();
39150             if(!s.focusNode || s.focusNode.nodeType != 3){
39151                 var r = s.getRangeAt(0);
39152                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
39153                 r.collapse(true);
39154                 this.deferFocus();
39155             }
39156             try{
39157                 this.execCmd('useCSS', true);
39158                 this.execCmd('styleWithCSS', false);
39159             }catch(e){}
39160         }
39161         this.fireEvent('activate', this);
39162     },
39163
39164     // private
39165     adjustFont: function(btn){
39166         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
39167         //if(Roo.isSafari){ // safari
39168         //    adjust *= 2;
39169        // }
39170         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
39171         if(Roo.isSafari){ // safari
39172             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
39173             v =  (v < 10) ? 10 : v;
39174             v =  (v > 48) ? 48 : v;
39175             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
39176             
39177         }
39178         
39179         
39180         v = Math.max(1, v+adjust);
39181         
39182         this.execCmd('FontSize', v  );
39183     },
39184
39185     onEditorEvent : function(e){
39186         this.fireEvent('editorevent', this, e);
39187       //  this.updateToolbar();
39188         this.syncValue();
39189     },
39190
39191     insertTag : function(tg)
39192     {
39193         // could be a bit smarter... -> wrap the current selected tRoo..
39194         
39195         this.execCmd("formatblock",   tg);
39196         
39197     },
39198     
39199     insertText : function(txt)
39200     {
39201         
39202         
39203         range = this.createRange();
39204         range.deleteContents();
39205                //alert(Sender.getAttribute('label'));
39206                
39207         range.insertNode(this.doc.createTextNode(txt));
39208     } ,
39209     
39210     // private
39211     relayBtnCmd : function(btn){
39212         this.relayCmd(btn.cmd);
39213     },
39214
39215     /**
39216      * Executes a Midas editor command on the editor document and performs necessary focus and
39217      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39218      * @param {String} cmd The Midas command
39219      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39220      */
39221     relayCmd : function(cmd, value){
39222         this.win.focus();
39223         this.execCmd(cmd, value);
39224         this.fireEvent('editorevent', this);
39225         //this.updateToolbar();
39226         this.deferFocus();
39227     },
39228
39229     /**
39230      * Executes a Midas editor command directly on the editor document.
39231      * For visual commands, you should use {@link #relayCmd} instead.
39232      * <b>This should only be called after the editor is initialized.</b>
39233      * @param {String} cmd The Midas command
39234      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39235      */
39236     execCmd : function(cmd, value){
39237         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39238         this.syncValue();
39239     },
39240
39241    
39242     /**
39243      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39244      * to insert tRoo.
39245      * @param {String} text
39246      */
39247     insertAtCursor : function(text){
39248         if(!this.activated){
39249             return;
39250         }
39251         if(Roo.isIE){
39252             this.win.focus();
39253             var r = this.doc.selection.createRange();
39254             if(r){
39255                 r.collapse(true);
39256                 r.pasteHTML(text);
39257                 this.syncValue();
39258                 this.deferFocus();
39259             }
39260         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39261             this.win.focus();
39262             this.execCmd('InsertHTML', text);
39263             this.deferFocus();
39264         }
39265     },
39266  // private
39267     mozKeyPress : function(e){
39268         if(e.ctrlKey){
39269             var c = e.getCharCode(), cmd;
39270           
39271             if(c > 0){
39272                 c = String.fromCharCode(c).toLowerCase();
39273                 switch(c){
39274                     case 'b':
39275                         cmd = 'bold';
39276                     break;
39277                     case 'i':
39278                         cmd = 'italic';
39279                     break;
39280                     case 'u':
39281                         cmd = 'underline';
39282                     case 'v':
39283                         this.cleanUpPaste.defer(100, this);
39284                         return;
39285                     break;
39286                 }
39287                 if(cmd){
39288                     this.win.focus();
39289                     this.execCmd(cmd);
39290                     this.deferFocus();
39291                     e.preventDefault();
39292                 }
39293                 
39294             }
39295         }
39296     },
39297
39298     // private
39299     fixKeys : function(){ // load time branching for fastest keydown performance
39300         if(Roo.isIE){
39301             return function(e){
39302                 var k = e.getKey(), r;
39303                 if(k == e.TAB){
39304                     e.stopEvent();
39305                     r = this.doc.selection.createRange();
39306                     if(r){
39307                         r.collapse(true);
39308                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39309                         this.deferFocus();
39310                     }
39311                     return;
39312                 }
39313                 
39314                 if(k == e.ENTER){
39315                     r = this.doc.selection.createRange();
39316                     if(r){
39317                         var target = r.parentElement();
39318                         if(!target || target.tagName.toLowerCase() != 'li'){
39319                             e.stopEvent();
39320                             r.pasteHTML('<br />');
39321                             r.collapse(false);
39322                             r.select();
39323                         }
39324                     }
39325                 }
39326                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39327                     this.cleanUpPaste.defer(100, this);
39328                     return;
39329                 }
39330                 
39331                 
39332             };
39333         }else if(Roo.isOpera){
39334             return function(e){
39335                 var k = e.getKey();
39336                 if(k == e.TAB){
39337                     e.stopEvent();
39338                     this.win.focus();
39339                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39340                     this.deferFocus();
39341                 }
39342                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39343                     this.cleanUpPaste.defer(100, this);
39344                     return;
39345                 }
39346                 
39347             };
39348         }else if(Roo.isSafari){
39349             return function(e){
39350                 var k = e.getKey();
39351                 
39352                 if(k == e.TAB){
39353                     e.stopEvent();
39354                     this.execCmd('InsertText','\t');
39355                     this.deferFocus();
39356                     return;
39357                 }
39358                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39359                     this.cleanUpPaste.defer(100, this);
39360                     return;
39361                 }
39362                 
39363              };
39364         }
39365     }(),
39366     
39367     getAllAncestors: function()
39368     {
39369         var p = this.getSelectedNode();
39370         var a = [];
39371         if (!p) {
39372             a.push(p); // push blank onto stack..
39373             p = this.getParentElement();
39374         }
39375         
39376         
39377         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39378             a.push(p);
39379             p = p.parentNode;
39380         }
39381         a.push(this.doc.body);
39382         return a;
39383     },
39384     lastSel : false,
39385     lastSelNode : false,
39386     
39387     
39388     getSelection : function() 
39389     {
39390         this.assignDocWin();
39391         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39392     },
39393     
39394     getSelectedNode: function() 
39395     {
39396         // this may only work on Gecko!!!
39397         
39398         // should we cache this!!!!
39399         
39400         
39401         
39402          
39403         var range = this.createRange(this.getSelection()).cloneRange();
39404         
39405         if (Roo.isIE) {
39406             var parent = range.parentElement();
39407             while (true) {
39408                 var testRange = range.duplicate();
39409                 testRange.moveToElementText(parent);
39410                 if (testRange.inRange(range)) {
39411                     break;
39412                 }
39413                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39414                     break;
39415                 }
39416                 parent = parent.parentElement;
39417             }
39418             return parent;
39419         }
39420         
39421         // is ancestor a text element.
39422         var ac =  range.commonAncestorContainer;
39423         if (ac.nodeType == 3) {
39424             ac = ac.parentNode;
39425         }
39426         
39427         var ar = ac.childNodes;
39428          
39429         var nodes = [];
39430         var other_nodes = [];
39431         var has_other_nodes = false;
39432         for (var i=0;i<ar.length;i++) {
39433             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39434                 continue;
39435             }
39436             // fullly contained node.
39437             
39438             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39439                 nodes.push(ar[i]);
39440                 continue;
39441             }
39442             
39443             // probably selected..
39444             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39445                 other_nodes.push(ar[i]);
39446                 continue;
39447             }
39448             // outer..
39449             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39450                 continue;
39451             }
39452             
39453             
39454             has_other_nodes = true;
39455         }
39456         if (!nodes.length && other_nodes.length) {
39457             nodes= other_nodes;
39458         }
39459         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39460             return false;
39461         }
39462         
39463         return nodes[0];
39464     },
39465     createRange: function(sel)
39466     {
39467         // this has strange effects when using with 
39468         // top toolbar - not sure if it's a great idea.
39469         //this.editor.contentWindow.focus();
39470         if (typeof sel != "undefined") {
39471             try {
39472                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39473             } catch(e) {
39474                 return this.doc.createRange();
39475             }
39476         } else {
39477             return this.doc.createRange();
39478         }
39479     },
39480     getParentElement: function()
39481     {
39482         
39483         this.assignDocWin();
39484         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39485         
39486         var range = this.createRange(sel);
39487          
39488         try {
39489             var p = range.commonAncestorContainer;
39490             while (p.nodeType == 3) { // text node
39491                 p = p.parentNode;
39492             }
39493             return p;
39494         } catch (e) {
39495             return null;
39496         }
39497     
39498     },
39499     /***
39500      *
39501      * Range intersection.. the hard stuff...
39502      *  '-1' = before
39503      *  '0' = hits..
39504      *  '1' = after.
39505      *         [ -- selected range --- ]
39506      *   [fail]                        [fail]
39507      *
39508      *    basically..
39509      *      if end is before start or  hits it. fail.
39510      *      if start is after end or hits it fail.
39511      *
39512      *   if either hits (but other is outside. - then it's not 
39513      *   
39514      *    
39515      **/
39516     
39517     
39518     // @see http://www.thismuchiknow.co.uk/?p=64.
39519     rangeIntersectsNode : function(range, node)
39520     {
39521         var nodeRange = node.ownerDocument.createRange();
39522         try {
39523             nodeRange.selectNode(node);
39524         } catch (e) {
39525             nodeRange.selectNodeContents(node);
39526         }
39527     
39528         var rangeStartRange = range.cloneRange();
39529         rangeStartRange.collapse(true);
39530     
39531         var rangeEndRange = range.cloneRange();
39532         rangeEndRange.collapse(false);
39533     
39534         var nodeStartRange = nodeRange.cloneRange();
39535         nodeStartRange.collapse(true);
39536     
39537         var nodeEndRange = nodeRange.cloneRange();
39538         nodeEndRange.collapse(false);
39539     
39540         return rangeStartRange.compareBoundaryPoints(
39541                  Range.START_TO_START, nodeEndRange) == -1 &&
39542                rangeEndRange.compareBoundaryPoints(
39543                  Range.START_TO_START, nodeStartRange) == 1;
39544         
39545          
39546     },
39547     rangeCompareNode : function(range, node)
39548     {
39549         var nodeRange = node.ownerDocument.createRange();
39550         try {
39551             nodeRange.selectNode(node);
39552         } catch (e) {
39553             nodeRange.selectNodeContents(node);
39554         }
39555         
39556         
39557         range.collapse(true);
39558     
39559         nodeRange.collapse(true);
39560      
39561         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
39562         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
39563          
39564         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
39565         
39566         var nodeIsBefore   =  ss == 1;
39567         var nodeIsAfter    = ee == -1;
39568         
39569         if (nodeIsBefore && nodeIsAfter)
39570             return 0; // outer
39571         if (!nodeIsBefore && nodeIsAfter)
39572             return 1; //right trailed.
39573         
39574         if (nodeIsBefore && !nodeIsAfter)
39575             return 2;  // left trailed.
39576         // fully contined.
39577         return 3;
39578     },
39579
39580     // private? - in a new class?
39581     cleanUpPaste :  function()
39582     {
39583         // cleans up the whole document..
39584       //  console.log('cleanuppaste');
39585         this.cleanUpChildren(this.doc.body);
39586         
39587         
39588     },
39589     cleanUpChildren : function (n)
39590     {
39591         if (!n.childNodes.length) {
39592             return;
39593         }
39594         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39595            this.cleanUpChild(n.childNodes[i]);
39596         }
39597     },
39598     
39599     
39600         
39601     
39602     cleanUpChild : function (node)
39603     {
39604         //console.log(node);
39605         if (node.nodeName == "#text") {
39606             // clean up silly Windows -- stuff?
39607             return; 
39608         }
39609         if (node.nodeName == "#comment") {
39610             node.parentNode.removeChild(node);
39611             // clean up silly Windows -- stuff?
39612             return; 
39613         }
39614         
39615         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39616             // remove node.
39617             node.parentNode.removeChild(node);
39618             return;
39619             
39620         }
39621         if (Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1) {
39622             this.cleanUpChildren(node);
39623             // inserts everything just before this node...
39624             while (node.childNodes.length) {
39625                 var cn = node.childNodes[0];
39626                 node.removeChild(cn);
39627                 node.parentNode.insertBefore(cn, node);
39628             }
39629             node.parentNode.removeChild(node);
39630             return;
39631         }
39632         
39633         if (!node.attributes || !node.attributes.length) {
39634             this.cleanUpChildren(node);
39635             return;
39636         }
39637         
39638         function cleanAttr(n,v)
39639         {
39640             
39641             if (v.match(/^\./) || v.match(/^\//)) {
39642                 return;
39643             }
39644             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39645                 return;
39646             }
39647             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39648             node.removeAttribute(n);
39649             
39650         }
39651         
39652         function cleanStyle(n,v)
39653         {
39654             if (v.match(/expression/)) { //XSS?? should we even bother..
39655                 node.removeAttribute(n);
39656                 return;
39657             }
39658             
39659             
39660             var parts = v.split(/;/);
39661             Roo.each(parts, function(p) {
39662                 p = p.replace(/\s+/g,'');
39663                 if (!p.length) {
39664                     return true;
39665                 }
39666                 var l = p.split(':').shift().replace(/\s+/g,'');
39667                 
39668                 // only allow 'c whitelisted system attributes'
39669                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39670                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39671                     node.removeAttribute(n);
39672                     return false;
39673                 }
39674                 return true;
39675             });
39676             
39677             
39678         }
39679         
39680         
39681         for (var i = node.attributes.length-1; i > -1 ; i--) {
39682             var a = node.attributes[i];
39683             //console.log(a);
39684             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39685                 node.removeAttribute(a.name);
39686                 return;
39687             }
39688             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39689                 cleanAttr(a.name,a.value); // fixme..
39690                 return;
39691             }
39692             if (a.name == 'style') {
39693                 cleanStyle(a.name,a.value);
39694             }
39695             /// clean up MS crap..
39696             if (a.name == 'class') {
39697                 if (a.value.match(/^Mso/)) {
39698                     node.className = '';
39699                 }
39700             }
39701             
39702             // style cleanup!?
39703             // class cleanup?
39704             
39705         }
39706         
39707         
39708         this.cleanUpChildren(node);
39709         
39710         
39711     }
39712     
39713     
39714     // hide stuff that is not compatible
39715     /**
39716      * @event blur
39717      * @hide
39718      */
39719     /**
39720      * @event change
39721      * @hide
39722      */
39723     /**
39724      * @event focus
39725      * @hide
39726      */
39727     /**
39728      * @event specialkey
39729      * @hide
39730      */
39731     /**
39732      * @cfg {String} fieldClass @hide
39733      */
39734     /**
39735      * @cfg {String} focusClass @hide
39736      */
39737     /**
39738      * @cfg {String} autoCreate @hide
39739      */
39740     /**
39741      * @cfg {String} inputType @hide
39742      */
39743     /**
39744      * @cfg {String} invalidClass @hide
39745      */
39746     /**
39747      * @cfg {String} invalidText @hide
39748      */
39749     /**
39750      * @cfg {String} msgFx @hide
39751      */
39752     /**
39753      * @cfg {String} validateOnBlur @hide
39754      */
39755 });
39756
39757 Roo.form.HtmlEditor.white = [
39758         'area', 'br', 'img', 'input', 'hr', 'wbr',
39759         
39760        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39761        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39762        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39763        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39764        'table',   'ul',         'xmp', 
39765        
39766        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39767       'thead',   'tr', 
39768      
39769       'dir', 'menu', 'ol', 'ul', 'dl',
39770        
39771       'embed',  'object'
39772 ];
39773
39774
39775 Roo.form.HtmlEditor.black = [
39776     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39777         'applet', // 
39778         'base',   'basefont', 'bgsound', 'blink',  'body', 
39779         'frame',  'frameset', 'head',    'html',   'ilayer', 
39780         'iframe', 'layer',  'link',     'meta',    'object',   
39781         'script', 'style' ,'title',  'xml' // clean later..
39782 ];
39783 Roo.form.HtmlEditor.clean = [
39784     'script', 'style', 'title', 'xml'
39785 ];
39786 Roo.form.HtmlEditor.remove = [
39787     'font'
39788 ];
39789 // attributes..
39790
39791 Roo.form.HtmlEditor.ablack = [
39792     'on'
39793 ];
39794     
39795 Roo.form.HtmlEditor.aclean = [ 
39796     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39797 ];
39798
39799 // protocols..
39800 Roo.form.HtmlEditor.pwhite= [
39801         'http',  'https',  'mailto'
39802 ];
39803
39804 // white listed style attributes.
39805 Roo.form.HtmlEditor.cwhite= [
39806         'text-align',
39807         'font-size'
39808 ];
39809
39810 // <script type="text/javascript">
39811 /*
39812  * Based on
39813  * Ext JS Library 1.1.1
39814  * Copyright(c) 2006-2007, Ext JS, LLC.
39815  *  
39816  
39817  */
39818
39819 /**
39820  * @class Roo.form.HtmlEditorToolbar1
39821  * Basic Toolbar
39822  * 
39823  * Usage:
39824  *
39825  new Roo.form.HtmlEditor({
39826     ....
39827     toolbars : [
39828         new Roo.form.HtmlEditorToolbar1({
39829             disable : { fonts: 1 , format: 1, ..., ... , ...],
39830             btns : [ .... ]
39831         })
39832     }
39833      
39834  * 
39835  * @cfg {Object} disable List of elements to disable..
39836  * @cfg {Array} btns List of additional buttons.
39837  * 
39838  * 
39839  * NEEDS Extra CSS? 
39840  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39841  */
39842  
39843 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39844 {
39845     
39846     Roo.apply(this, config);
39847     
39848     // default disabled, based on 'good practice'..
39849     this.disable = this.disable || {};
39850     Roo.applyIf(this.disable, {
39851         fontSize : true,
39852         colors : true,
39853         specialElements : true
39854     });
39855     
39856     
39857     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39858     // dont call parent... till later.
39859 }
39860
39861 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39862     
39863     tb: false,
39864     
39865     rendered: false,
39866     
39867     editor : false,
39868     /**
39869      * @cfg {Object} disable  List of toolbar elements to disable
39870          
39871      */
39872     disable : false,
39873       /**
39874      * @cfg {Array} fontFamilies An array of available font families
39875      */
39876     fontFamilies : [
39877         'Arial',
39878         'Courier New',
39879         'Tahoma',
39880         'Times New Roman',
39881         'Verdana'
39882     ],
39883     
39884     specialChars : [
39885            "&#169;",
39886           "&#174;",     
39887           "&#8482;",    
39888           "&#163;" ,    
39889          // "&#8212;",    
39890           "&#8230;",    
39891           "&#247;" ,    
39892         //  "&#225;" ,     ?? a acute?
39893            "&#8364;"    , //Euro
39894        //   "&#8220;"    ,
39895         //  "&#8221;"    ,
39896         //  "&#8226;"    ,
39897           "&#176;"  //   , // degrees
39898
39899          // "&#233;"     , // e ecute
39900          // "&#250;"     , // u ecute?
39901     ],
39902     
39903     specialElements : [
39904         {
39905             text: "Insert Table",
39906             xtype: 'MenuItem',
39907             xns : Roo.Menu,
39908             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
39909                 
39910         },
39911         {    
39912             text: "Insert Image",
39913             xtype: 'MenuItem',
39914             xns : Roo.Menu,
39915             ihtml : '<img src="about:blank"/>'
39916             
39917         }
39918         
39919          
39920     ],
39921     
39922     
39923     inputElements : [ 
39924             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39925             "input:submit", "input:button", "select", "textarea", "label" ],
39926     formats : [
39927         ["p"] ,  
39928         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39929         ["pre"],[ "code"], 
39930         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39931     ],
39932      /**
39933      * @cfg {String} defaultFont default font to use.
39934      */
39935     defaultFont: 'tahoma',
39936    
39937     fontSelect : false,
39938     
39939     
39940     formatCombo : false,
39941     
39942     init : function(editor)
39943     {
39944         this.editor = editor;
39945         
39946         
39947         var fid = editor.frameId;
39948         var etb = this;
39949         function btn(id, toggle, handler){
39950             var xid = fid + '-'+ id ;
39951             return {
39952                 id : xid,
39953                 cmd : id,
39954                 cls : 'x-btn-icon x-edit-'+id,
39955                 enableToggle:toggle !== false,
39956                 scope: editor, // was editor...
39957                 handler:handler||editor.relayBtnCmd,
39958                 clickEvent:'mousedown',
39959                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39960                 tabIndex:-1
39961             };
39962         }
39963         
39964         
39965         
39966         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39967         this.tb = tb;
39968          // stop form submits
39969         tb.el.on('click', function(e){
39970             e.preventDefault(); // what does this do?
39971         });
39972
39973         if(!this.disable.font && !Roo.isSafari){
39974             /* why no safari for fonts
39975             editor.fontSelect = tb.el.createChild({
39976                 tag:'select',
39977                 tabIndex: -1,
39978                 cls:'x-font-select',
39979                 html: editor.createFontOptions()
39980             });
39981             editor.fontSelect.on('change', function(){
39982                 var font = editor.fontSelect.dom.value;
39983                 editor.relayCmd('fontname', font);
39984                 editor.deferFocus();
39985             }, editor);
39986             tb.add(
39987                 editor.fontSelect.dom,
39988                 '-'
39989             );
39990             */
39991         };
39992         if(!this.disable.formats){
39993             this.formatCombo = new Roo.form.ComboBox({
39994                 store: new Roo.data.SimpleStore({
39995                     id : 'tag',
39996                     fields: ['tag'],
39997                     data : this.formats // from states.js
39998                 }),
39999                 blockFocus : true,
40000                 //autoCreate : {tag: "div",  size: "20"},
40001                 displayField:'tag',
40002                 typeAhead: false,
40003                 mode: 'local',
40004                 editable : false,
40005                 triggerAction: 'all',
40006                 emptyText:'Add tag',
40007                 selectOnFocus:true,
40008                 width:135,
40009                 listeners : {
40010                     'select': function(c, r, i) {
40011                         editor.insertTag(r.get('tag'));
40012                         editor.focus();
40013                     }
40014                 }
40015
40016             });
40017             tb.addField(this.formatCombo);
40018             
40019         }
40020         
40021         if(!this.disable.format){
40022             tb.add(
40023                 btn('bold'),
40024                 btn('italic'),
40025                 btn('underline')
40026             );
40027         };
40028         if(!this.disable.fontSize){
40029             tb.add(
40030                 '-',
40031                 
40032                 
40033                 btn('increasefontsize', false, editor.adjustFont),
40034                 btn('decreasefontsize', false, editor.adjustFont)
40035             );
40036         };
40037         
40038         
40039         if(!this.disable.colors){
40040             tb.add(
40041                 '-', {
40042                     id:editor.frameId +'-forecolor',
40043                     cls:'x-btn-icon x-edit-forecolor',
40044                     clickEvent:'mousedown',
40045                     tooltip: this.buttonTips['forecolor'] || undefined,
40046                     tabIndex:-1,
40047                     menu : new Roo.menu.ColorMenu({
40048                         allowReselect: true,
40049                         focus: Roo.emptyFn,
40050                         value:'000000',
40051                         plain:true,
40052                         selectHandler: function(cp, color){
40053                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
40054                             editor.deferFocus();
40055                         },
40056                         scope: editor,
40057                         clickEvent:'mousedown'
40058                     })
40059                 }, {
40060                     id:editor.frameId +'backcolor',
40061                     cls:'x-btn-icon x-edit-backcolor',
40062                     clickEvent:'mousedown',
40063                     tooltip: this.buttonTips['backcolor'] || undefined,
40064                     tabIndex:-1,
40065                     menu : new Roo.menu.ColorMenu({
40066                         focus: Roo.emptyFn,
40067                         value:'FFFFFF',
40068                         plain:true,
40069                         allowReselect: true,
40070                         selectHandler: function(cp, color){
40071                             if(Roo.isGecko){
40072                                 editor.execCmd('useCSS', false);
40073                                 editor.execCmd('hilitecolor', color);
40074                                 editor.execCmd('useCSS', true);
40075                                 editor.deferFocus();
40076                             }else{
40077                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
40078                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
40079                                 editor.deferFocus();
40080                             }
40081                         },
40082                         scope:editor,
40083                         clickEvent:'mousedown'
40084                     })
40085                 }
40086             );
40087         };
40088         // now add all the items...
40089         
40090
40091         if(!this.disable.alignments){
40092             tb.add(
40093                 '-',
40094                 btn('justifyleft'),
40095                 btn('justifycenter'),
40096                 btn('justifyright')
40097             );
40098         };
40099
40100         //if(!Roo.isSafari){
40101             if(!this.disable.links){
40102                 tb.add(
40103                     '-',
40104                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40105                 );
40106             };
40107
40108             if(!this.disable.lists){
40109                 tb.add(
40110                     '-',
40111                     btn('insertorderedlist'),
40112                     btn('insertunorderedlist')
40113                 );
40114             }
40115             if(!this.disable.sourceEdit){
40116                 tb.add(
40117                     '-',
40118                     btn('sourceedit', true, function(btn){
40119                         this.toggleSourceEdit(btn.pressed);
40120                     })
40121                 );
40122             }
40123         //}
40124         
40125         var smenu = { };
40126         // special menu.. - needs to be tidied up..
40127         if (!this.disable.special) {
40128             smenu = {
40129                 text: "&#169;",
40130                 cls: 'x-edit-none',
40131                 
40132                 menu : {
40133                     items : []
40134                 }
40135             };
40136             for (var i =0; i < this.specialChars.length; i++) {
40137                 smenu.menu.items.push({
40138                     
40139                     html: this.specialChars[i],
40140                     handler: function(a,b) {
40141                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40142                         
40143                     },
40144                     tabIndex:-1
40145                 });
40146             }
40147             
40148             
40149             tb.add(smenu);
40150             
40151             
40152         }
40153          
40154         if (!this.disable.specialElements) {
40155             var semenu = {
40156                 text: "Other;",
40157                 cls: 'x-edit-none',
40158                 menu : {
40159                     items : []
40160                 }
40161             };
40162             for (var i =0; i < this.specialElements.length; i++) {
40163                 semenu.menu.items.push(
40164                     Roo.apply({ 
40165                         handler: function(a,b) {
40166                             editor.insertAtCursor(this.ihtml);
40167                         }
40168                     }, this.specialElements[i])
40169                 );
40170                     
40171             }
40172             
40173             tb.add(semenu);
40174             
40175             
40176         }
40177          
40178         
40179         if (this.btns) {
40180             for(var i =0; i< this.btns.length;i++) {
40181                 var b = this.btns[i];
40182                 b.cls =  'x-edit-none';
40183                 b.scope = editor;
40184                 tb.add(b);
40185             }
40186         
40187         }
40188         
40189         
40190         
40191         // disable everything...
40192         
40193         this.tb.items.each(function(item){
40194            if(item.id != editor.frameId+ '-sourceedit'){
40195                 item.disable();
40196             }
40197         });
40198         this.rendered = true;
40199         
40200         // the all the btns;
40201         editor.on('editorevent', this.updateToolbar, this);
40202         // other toolbars need to implement this..
40203         //editor.on('editmodechange', this.updateToolbar, this);
40204     },
40205     
40206     
40207     
40208     /**
40209      * Protected method that will not generally be called directly. It triggers
40210      * a toolbar update by reading the markup state of the current selection in the editor.
40211      */
40212     updateToolbar: function(){
40213
40214         if(!this.editor.activated){
40215             this.editor.onFirstFocus();
40216             return;
40217         }
40218
40219         var btns = this.tb.items.map, 
40220             doc = this.editor.doc,
40221             frameId = this.editor.frameId;
40222
40223         if(!this.disable.font && !Roo.isSafari){
40224             /*
40225             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40226             if(name != this.fontSelect.dom.value){
40227                 this.fontSelect.dom.value = name;
40228             }
40229             */
40230         }
40231         if(!this.disable.format){
40232             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40233             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40234             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40235         }
40236         if(!this.disable.alignments){
40237             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40238             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40239             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40240         }
40241         if(!Roo.isSafari && !this.disable.lists){
40242             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40243             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40244         }
40245         
40246         var ans = this.editor.getAllAncestors();
40247         if (this.formatCombo) {
40248             
40249             
40250             var store = this.formatCombo.store;
40251             this.formatCombo.setValue("");
40252             for (var i =0; i < ans.length;i++) {
40253                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40254                     // select it..
40255                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40256                     break;
40257                 }
40258             }
40259         }
40260         
40261         
40262         
40263         // hides menus... - so this cant be on a menu...
40264         Roo.menu.MenuMgr.hideAll();
40265
40266         //this.editorsyncValue();
40267     },
40268    
40269     
40270     createFontOptions : function(){
40271         var buf = [], fs = this.fontFamilies, ff, lc;
40272         for(var i = 0, len = fs.length; i< len; i++){
40273             ff = fs[i];
40274             lc = ff.toLowerCase();
40275             buf.push(
40276                 '<option value="',lc,'" style="font-family:',ff,';"',
40277                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40278                     ff,
40279                 '</option>'
40280             );
40281         }
40282         return buf.join('');
40283     },
40284     
40285     toggleSourceEdit : function(sourceEditMode){
40286         if(sourceEditMode === undefined){
40287             sourceEditMode = !this.sourceEditMode;
40288         }
40289         this.sourceEditMode = sourceEditMode === true;
40290         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40291         // just toggle the button?
40292         if(btn.pressed !== this.editor.sourceEditMode){
40293             btn.toggle(this.editor.sourceEditMode);
40294             return;
40295         }
40296         
40297         if(this.sourceEditMode){
40298             this.tb.items.each(function(item){
40299                 if(item.cmd != 'sourceedit'){
40300                     item.disable();
40301                 }
40302             });
40303           
40304         }else{
40305             if(this.initialized){
40306                 this.tb.items.each(function(item){
40307                     item.enable();
40308                 });
40309             }
40310             
40311         }
40312         // tell the editor that it's been pressed..
40313         this.editor.toggleSourceEdit(sourceEditMode);
40314        
40315     },
40316      /**
40317      * Object collection of toolbar tooltips for the buttons in the editor. The key
40318      * is the command id associated with that button and the value is a valid QuickTips object.
40319      * For example:
40320 <pre><code>
40321 {
40322     bold : {
40323         title: 'Bold (Ctrl+B)',
40324         text: 'Make the selected text bold.',
40325         cls: 'x-html-editor-tip'
40326     },
40327     italic : {
40328         title: 'Italic (Ctrl+I)',
40329         text: 'Make the selected text italic.',
40330         cls: 'x-html-editor-tip'
40331     },
40332     ...
40333 </code></pre>
40334     * @type Object
40335      */
40336     buttonTips : {
40337         bold : {
40338             title: 'Bold (Ctrl+B)',
40339             text: 'Make the selected text bold.',
40340             cls: 'x-html-editor-tip'
40341         },
40342         italic : {
40343             title: 'Italic (Ctrl+I)',
40344             text: 'Make the selected text italic.',
40345             cls: 'x-html-editor-tip'
40346         },
40347         underline : {
40348             title: 'Underline (Ctrl+U)',
40349             text: 'Underline the selected text.',
40350             cls: 'x-html-editor-tip'
40351         },
40352         increasefontsize : {
40353             title: 'Grow Text',
40354             text: 'Increase the font size.',
40355             cls: 'x-html-editor-tip'
40356         },
40357         decreasefontsize : {
40358             title: 'Shrink Text',
40359             text: 'Decrease the font size.',
40360             cls: 'x-html-editor-tip'
40361         },
40362         backcolor : {
40363             title: 'Text Highlight Color',
40364             text: 'Change the background color of the selected text.',
40365             cls: 'x-html-editor-tip'
40366         },
40367         forecolor : {
40368             title: 'Font Color',
40369             text: 'Change the color of the selected text.',
40370             cls: 'x-html-editor-tip'
40371         },
40372         justifyleft : {
40373             title: 'Align Text Left',
40374             text: 'Align text to the left.',
40375             cls: 'x-html-editor-tip'
40376         },
40377         justifycenter : {
40378             title: 'Center Text',
40379             text: 'Center text in the editor.',
40380             cls: 'x-html-editor-tip'
40381         },
40382         justifyright : {
40383             title: 'Align Text Right',
40384             text: 'Align text to the right.',
40385             cls: 'x-html-editor-tip'
40386         },
40387         insertunorderedlist : {
40388             title: 'Bullet List',
40389             text: 'Start a bulleted list.',
40390             cls: 'x-html-editor-tip'
40391         },
40392         insertorderedlist : {
40393             title: 'Numbered List',
40394             text: 'Start a numbered list.',
40395             cls: 'x-html-editor-tip'
40396         },
40397         createlink : {
40398             title: 'Hyperlink',
40399             text: 'Make the selected text a hyperlink.',
40400             cls: 'x-html-editor-tip'
40401         },
40402         sourceedit : {
40403             title: 'Source Edit',
40404             text: 'Switch to source editing mode.',
40405             cls: 'x-html-editor-tip'
40406         }
40407     },
40408     // private
40409     onDestroy : function(){
40410         if(this.rendered){
40411             
40412             this.tb.items.each(function(item){
40413                 if(item.menu){
40414                     item.menu.removeAll();
40415                     if(item.menu.el){
40416                         item.menu.el.destroy();
40417                     }
40418                 }
40419                 item.destroy();
40420             });
40421              
40422         }
40423     },
40424     onFirstFocus: function() {
40425         this.tb.items.each(function(item){
40426            item.enable();
40427         });
40428     }
40429 });
40430
40431
40432
40433
40434 // <script type="text/javascript">
40435 /*
40436  * Based on
40437  * Ext JS Library 1.1.1
40438  * Copyright(c) 2006-2007, Ext JS, LLC.
40439  *  
40440  
40441  */
40442
40443  
40444 /**
40445  * @class Roo.form.HtmlEditor.ToolbarContext
40446  * Context Toolbar
40447  * 
40448  * Usage:
40449  *
40450  new Roo.form.HtmlEditor({
40451     ....
40452     toolbars : [
40453         { xtype: 'ToolbarStandard', styles : {} }
40454         { xtype: 'ToolbarContext', disable : {} }
40455     ]
40456 })
40457
40458      
40459  * 
40460  * @config : {Object} disable List of elements to disable.. (not done yet.)
40461  * @config : {Object} styles  Map of styles available.
40462  * 
40463  */
40464
40465 Roo.form.HtmlEditor.ToolbarContext = function(config)
40466 {
40467     
40468     Roo.apply(this, config);
40469     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40470     // dont call parent... till later.
40471     this.styles = this.styles || {};
40472 }
40473 Roo.form.HtmlEditor.ToolbarContext.types = {
40474     'IMG' : {
40475         width : {
40476             title: "Width",
40477             width: 40
40478         },
40479         height:  {
40480             title: "Height",
40481             width: 40
40482         },
40483         align: {
40484             title: "Align",
40485             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40486             width : 80
40487             
40488         },
40489         border: {
40490             title: "Border",
40491             width: 40
40492         },
40493         alt: {
40494             title: "Alt",
40495             width: 120
40496         },
40497         src : {
40498             title: "Src",
40499             width: 220
40500         }
40501         
40502     },
40503     'A' : {
40504         name : {
40505             title: "Name",
40506             width: 50
40507         },
40508         href:  {
40509             title: "Href",
40510             width: 220
40511         } // border?
40512         
40513     },
40514     'TABLE' : {
40515         rows : {
40516             title: "Rows",
40517             width: 20
40518         },
40519         cols : {
40520             title: "Cols",
40521             width: 20
40522         },
40523         width : {
40524             title: "Width",
40525             width: 40
40526         },
40527         height : {
40528             title: "Height",
40529             width: 40
40530         },
40531         border : {
40532             title: "Border",
40533             width: 20
40534         }
40535     },
40536     'TD' : {
40537         width : {
40538             title: "Width",
40539             width: 40
40540         },
40541         height : {
40542             title: "Height",
40543             width: 40
40544         },   
40545         align: {
40546             title: "Align",
40547             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40548             width: 80
40549         },
40550         valign: {
40551             title: "Valign",
40552             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40553             width: 80
40554         },
40555         colspan: {
40556             title: "Colspan",
40557             width: 20
40558             
40559         }
40560     },
40561     'INPUT' : {
40562         name : {
40563             title: "name",
40564             width: 120
40565         },
40566         value : {
40567             title: "Value",
40568             width: 120
40569         },
40570         width : {
40571             title: "Width",
40572             width: 40
40573         }
40574     },
40575     'LABEL' : {
40576         'for' : {
40577             title: "For",
40578             width: 120
40579         }
40580     },
40581     'TEXTAREA' : {
40582           name : {
40583             title: "name",
40584             width: 120
40585         },
40586         rows : {
40587             title: "Rows",
40588             width: 20
40589         },
40590         cols : {
40591             title: "Cols",
40592             width: 20
40593         }
40594     },
40595     'SELECT' : {
40596         name : {
40597             title: "name",
40598             width: 120
40599         },
40600         selectoptions : {
40601             title: "Options",
40602             width: 200
40603         }
40604     },
40605     
40606     // should we really allow this??
40607     // should this just be 
40608     'BODY' : {
40609         title : {
40610             title: "title",
40611             width: 200,
40612             disabled : true
40613         }
40614     },
40615     '*' : {
40616         // empty..
40617     }
40618 };
40619
40620
40621
40622 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40623     
40624     tb: false,
40625     
40626     rendered: false,
40627     
40628     editor : false,
40629     /**
40630      * @cfg {Object} disable  List of toolbar elements to disable
40631          
40632      */
40633     disable : false,
40634     /**
40635      * @cfg {Object} styles List of styles 
40636      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
40637      *
40638      * These must be defined in the page, so they get rendered correctly..
40639      * .headline { }
40640      * TD.underline { }
40641      * 
40642      */
40643     styles : false,
40644     
40645     
40646     
40647     toolbars : false,
40648     
40649     init : function(editor)
40650     {
40651         this.editor = editor;
40652         
40653         
40654         var fid = editor.frameId;
40655         var etb = this;
40656         function btn(id, toggle, handler){
40657             var xid = fid + '-'+ id ;
40658             return {
40659                 id : xid,
40660                 cmd : id,
40661                 cls : 'x-btn-icon x-edit-'+id,
40662                 enableToggle:toggle !== false,
40663                 scope: editor, // was editor...
40664                 handler:handler||editor.relayBtnCmd,
40665                 clickEvent:'mousedown',
40666                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40667                 tabIndex:-1
40668             };
40669         }
40670         // create a new element.
40671         var wdiv = editor.wrap.createChild({
40672                 tag: 'div'
40673             }, editor.wrap.dom.firstChild.nextSibling, true);
40674         
40675         // can we do this more than once??
40676         
40677          // stop form submits
40678       
40679  
40680         // disable everything...
40681         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40682         this.toolbars = {};
40683            
40684         for (var i in  ty) {
40685           
40686             this.toolbars[i] = this.buildToolbar(ty[i],i);
40687         }
40688         this.tb = this.toolbars.BODY;
40689         this.tb.el.show();
40690         this.buildFooter();
40691         this.footer.show();
40692          
40693         this.rendered = true;
40694         
40695         // the all the btns;
40696         editor.on('editorevent', this.updateToolbar, this);
40697         // other toolbars need to implement this..
40698         //editor.on('editmodechange', this.updateToolbar, this);
40699     },
40700     
40701     
40702     
40703     /**
40704      * Protected method that will not generally be called directly. It triggers
40705      * a toolbar update by reading the markup state of the current selection in the editor.
40706      */
40707     updateToolbar: function(ignore_a,ignore_b,sel){
40708
40709         
40710         if(!this.editor.activated){
40711              this.editor.onFirstFocus();
40712             return;
40713         }
40714         var updateFooter = sel ? false : true;
40715         
40716         
40717         var ans = this.editor.getAllAncestors();
40718         
40719         // pick
40720         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40721         
40722         if (!sel) { 
40723             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40724             sel = sel ? sel : this.editor.doc.body;
40725             sel = sel.tagName.length ? sel : this.editor.doc.body;
40726             
40727         }
40728         // pick a menu that exists..
40729         var tn = sel.tagName.toUpperCase();
40730         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40731         
40732         tn = sel.tagName.toUpperCase();
40733         
40734         var lastSel = this.tb.selectedNode
40735         
40736         this.tb.selectedNode = sel;
40737         
40738         // if current menu does not match..
40739         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
40740                 
40741             this.tb.el.hide();
40742             ///console.log("show: " + tn);
40743             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
40744             this.tb.el.show();
40745             // update name
40746             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
40747             
40748             
40749             // update attributes
40750             if (this.tb.fields) {
40751                 this.tb.fields.each(function(e) {
40752                    e.setValue(sel.getAttribute(e.name));
40753                 });
40754             }
40755             
40756             // update styles
40757             var st = this.tb.fields.item(0);
40758             st.store.removeAll();
40759             var cn = sel.className.split(/\s+/);
40760             
40761             var avs = [];
40762             if (this.styles['*']) {
40763                 
40764                 Roo.each(this.styles['*'], function(v) {
40765                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40766                 });
40767             }
40768             if (this.styles[tn]) { 
40769                 Roo.each(this.styles[tn], function(v) {
40770                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40771                 });
40772             }
40773             
40774             st.store.loadData(avs);
40775             st.collapse();
40776             st.setValue(cn);
40777             
40778             // flag our selected Node.
40779             this.tb.selectedNode = sel;
40780            
40781            
40782             Roo.menu.MenuMgr.hideAll();
40783
40784         }
40785         
40786         if (!updateFooter) {
40787             return;
40788         }
40789         // update the footer
40790         //
40791         var html = '';
40792         
40793         this.footerEls = ans.reverse();
40794         Roo.each(this.footerEls, function(a,i) {
40795             if (!a) { return; }
40796             html += html.length ? ' &gt; '  :  '';
40797             
40798             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
40799             
40800         });
40801        
40802         // 
40803         var sz = this.footDisp.up('td').getSize();
40804         this.footDisp.dom.style.width = (sz.width -10) + 'px';
40805         this.footDisp.dom.style.marginLeft = '5px';
40806         
40807         this.footDisp.dom.style.overflow = 'hidden';
40808         
40809         this.footDisp.dom.innerHTML = html;
40810             
40811         //this.editorsyncValue();
40812     },
40813    
40814        
40815     // private
40816     onDestroy : function(){
40817         if(this.rendered){
40818             
40819             this.tb.items.each(function(item){
40820                 if(item.menu){
40821                     item.menu.removeAll();
40822                     if(item.menu.el){
40823                         item.menu.el.destroy();
40824                     }
40825                 }
40826                 item.destroy();
40827             });
40828              
40829         }
40830     },
40831     onFirstFocus: function() {
40832         // need to do this for all the toolbars..
40833         this.tb.items.each(function(item){
40834            item.enable();
40835         });
40836     },
40837     buildToolbar: function(tlist, nm)
40838     {
40839         var editor = this.editor;
40840          // create a new element.
40841         var wdiv = editor.wrap.createChild({
40842                 tag: 'div'
40843             }, editor.wrap.dom.firstChild.nextSibling, true);
40844         
40845        
40846         var tb = new Roo.Toolbar(wdiv);
40847         // add the name..
40848         
40849         tb.add(nm+ ":&nbsp;");
40850         
40851         // styles...
40852         if (this.styles) {
40853             
40854             // this needs a multi-select checkbox...
40855             tb.addField( new Roo.form.ComboBox({
40856                 store: new Roo.data.SimpleStore({
40857                     id : 'val',
40858                     fields: ['val', 'selected'],
40859                     data : [] 
40860                 }),
40861                 name : 'className',
40862                 displayField:'val',
40863                 typeAhead: false,
40864                 mode: 'local',
40865                 editable : false,
40866                 triggerAction: 'all',
40867                 emptyText:'Select Style',
40868                 selectOnFocus:true,
40869                 width: 130,
40870                 listeners : {
40871                     'select': function(c, r, i) {
40872                         // initial support only for on class per el..
40873                         tb.selectedNode.className =  r ? r.get('val') : '';
40874                     }
40875                 }
40876     
40877             }));
40878         }
40879             
40880         
40881         
40882         for (var i in tlist) {
40883             
40884             var item = tlist[i];
40885             tb.add(item.title + ":&nbsp;");
40886             
40887             
40888             
40889             
40890             if (item.opts) {
40891                 // opts == pulldown..
40892                 tb.addField( new Roo.form.ComboBox({
40893                     store: new Roo.data.SimpleStore({
40894                         id : 'val',
40895                         fields: ['val'],
40896                         data : item.opts  
40897                     }),
40898                     name : i,
40899                     displayField:'val',
40900                     typeAhead: false,
40901                     mode: 'local',
40902                     editable : false,
40903                     triggerAction: 'all',
40904                     emptyText:'Select',
40905                     selectOnFocus:true,
40906                     width: item.width ? item.width  : 130,
40907                     listeners : {
40908                         'select': function(c, r, i) {
40909                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40910                         }
40911                     }
40912
40913                 }));
40914                 continue;
40915                     
40916                  
40917                 
40918                 tb.addField( new Roo.form.TextField({
40919                     name: i,
40920                     width: 100,
40921                     //allowBlank:false,
40922                     value: ''
40923                 }));
40924                 continue;
40925             }
40926             tb.addField( new Roo.form.TextField({
40927                 name: i,
40928                 width: item.width,
40929                 //allowBlank:true,
40930                 value: '',
40931                 listeners: {
40932                     'change' : function(f, nv, ov) {
40933                         tb.selectedNode.setAttribute(f.name, nv);
40934                     }
40935                 }
40936             }));
40937              
40938         }
40939         tb.el.on('click', function(e){
40940             e.preventDefault(); // what does this do?
40941         });
40942         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40943         tb.el.hide();
40944         tb.name = nm;
40945         // dont need to disable them... as they will get hidden
40946         return tb;
40947          
40948         
40949     },
40950     buildFooter : function()
40951     {
40952         
40953         var fel = this.editor.wrap.createChild();
40954         this.footer = new Roo.Toolbar(fel);
40955         // toolbar has scrolly on left / right?
40956         var footDisp= new Roo.Toolbar.Fill();
40957         var _t = this;
40958         this.footer.add(
40959             {
40960                 text : '&lt;',
40961                 xtype: 'Button',
40962                 handler : function() {
40963                     _t.footDisp.scrollTo('left',0,true)
40964                 }
40965             }
40966         );
40967         this.footer.add( footDisp );
40968         this.footer.add( 
40969             {
40970                 text : '&gt;',
40971                 xtype: 'Button',
40972                 handler : function() {
40973                     // no animation..
40974                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
40975                 }
40976             }
40977         );
40978         var fel = Roo.get(footDisp.el);
40979         fel.addClass('x-editor-context');
40980         this.footDispWrap = fel; 
40981         this.footDispWrap.overflow  = 'hidden';
40982         
40983         this.footDisp = fel.createChild();
40984         this.footDispWrap.on('click', this.onContextClick, this)
40985         
40986         
40987     },
40988     onContextClick : function (ev,dom)
40989     {
40990         ev.preventDefault();
40991         var  cn = dom.className;
40992         Roo.log(cn);
40993         if (!cn.match(/x-ed-loc-/)) {
40994             return;
40995         }
40996         var n = cn.split('-').pop();
40997         var ans = this.footerEls;
40998         var sel = ans[n];
40999         
41000          // pick
41001         var range = this.editor.createRange();
41002         
41003         range.selectNodeContents(sel);
41004         //range.selectNode(sel);
41005         
41006         
41007         var selection = this.editor.getSelection();
41008         selection.removeAllRanges();
41009         selection.addRange(range);
41010         
41011         
41012         
41013         this.updateToolbar(null, null, sel);
41014         
41015         
41016     }
41017     
41018     
41019     
41020     
41021     
41022 });
41023
41024
41025
41026
41027
41028 /*
41029  * Based on:
41030  * Ext JS Library 1.1.1
41031  * Copyright(c) 2006-2007, Ext JS, LLC.
41032  *
41033  * Originally Released Under LGPL - original licence link has changed is not relivant.
41034  *
41035  * Fork - LGPL
41036  * <script type="text/javascript">
41037  */
41038  
41039 /**
41040  * @class Roo.form.BasicForm
41041  * @extends Roo.util.Observable
41042  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
41043  * @constructor
41044  * @param {String/HTMLElement/Roo.Element} el The form element or its id
41045  * @param {Object} config Configuration options
41046  */
41047 Roo.form.BasicForm = function(el, config){
41048     this.allItems = [];
41049     this.childForms = [];
41050     Roo.apply(this, config);
41051     /*
41052      * The Roo.form.Field items in this form.
41053      * @type MixedCollection
41054      */
41055      
41056      
41057     this.items = new Roo.util.MixedCollection(false, function(o){
41058         return o.id || (o.id = Roo.id());
41059     });
41060     this.addEvents({
41061         /**
41062          * @event beforeaction
41063          * Fires before any action is performed. Return false to cancel the action.
41064          * @param {Form} this
41065          * @param {Action} action The action to be performed
41066          */
41067         beforeaction: true,
41068         /**
41069          * @event actionfailed
41070          * Fires when an action fails.
41071          * @param {Form} this
41072          * @param {Action} action The action that failed
41073          */
41074         actionfailed : true,
41075         /**
41076          * @event actioncomplete
41077          * Fires when an action is completed.
41078          * @param {Form} this
41079          * @param {Action} action The action that completed
41080          */
41081         actioncomplete : true
41082     });
41083     if(el){
41084         this.initEl(el);
41085     }
41086     Roo.form.BasicForm.superclass.constructor.call(this);
41087 };
41088
41089 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41090     /**
41091      * @cfg {String} method
41092      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41093      */
41094     /**
41095      * @cfg {DataReader} reader
41096      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41097      * This is optional as there is built-in support for processing JSON.
41098      */
41099     /**
41100      * @cfg {DataReader} errorReader
41101      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41102      * This is completely optional as there is built-in support for processing JSON.
41103      */
41104     /**
41105      * @cfg {String} url
41106      * The URL to use for form actions if one isn't supplied in the action options.
41107      */
41108     /**
41109      * @cfg {Boolean} fileUpload
41110      * Set to true if this form is a file upload.
41111      */
41112      
41113     /**
41114      * @cfg {Object} baseParams
41115      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41116      */
41117      /**
41118      
41119     /**
41120      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41121      */
41122     timeout: 30,
41123
41124     // private
41125     activeAction : null,
41126
41127     /**
41128      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41129      * or setValues() data instead of when the form was first created.
41130      */
41131     trackResetOnLoad : false,
41132     
41133     
41134     /**
41135      * childForms - used for multi-tab forms
41136      * @type {Array}
41137      */
41138     childForms : false,
41139     
41140     /**
41141      * allItems - full list of fields.
41142      * @type {Array}
41143      */
41144     allItems : false,
41145     
41146     /**
41147      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41148      * element by passing it or its id or mask the form itself by passing in true.
41149      * @type Mixed
41150      */
41151     waitMsgTarget : false,
41152
41153     // private
41154     initEl : function(el){
41155         this.el = Roo.get(el);
41156         this.id = this.el.id || Roo.id();
41157         this.el.on('submit', this.onSubmit, this);
41158         this.el.addClass('x-form');
41159     },
41160
41161     // private
41162     onSubmit : function(e){
41163         e.stopEvent();
41164     },
41165
41166     /**
41167      * Returns true if client-side validation on the form is successful.
41168      * @return Boolean
41169      */
41170     isValid : function(){
41171         var valid = true;
41172         this.items.each(function(f){
41173            if(!f.validate()){
41174                valid = false;
41175            }
41176         });
41177         return valid;
41178     },
41179
41180     /**
41181      * Returns true if any fields in this form have changed since their original load.
41182      * @return Boolean
41183      */
41184     isDirty : function(){
41185         var dirty = false;
41186         this.items.each(function(f){
41187            if(f.isDirty()){
41188                dirty = true;
41189                return false;
41190            }
41191         });
41192         return dirty;
41193     },
41194
41195     /**
41196      * Performs a predefined action (submit or load) or custom actions you define on this form.
41197      * @param {String} actionName The name of the action type
41198      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41199      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41200      * accept other config options):
41201      * <pre>
41202 Property          Type             Description
41203 ----------------  ---------------  ----------------------------------------------------------------------------------
41204 url               String           The url for the action (defaults to the form's url)
41205 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41206 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41207 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41208                                    validate the form on the client (defaults to false)
41209      * </pre>
41210      * @return {BasicForm} this
41211      */
41212     doAction : function(action, options){
41213         if(typeof action == 'string'){
41214             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41215         }
41216         if(this.fireEvent('beforeaction', this, action) !== false){
41217             this.beforeAction(action);
41218             action.run.defer(100, action);
41219         }
41220         return this;
41221     },
41222
41223     /**
41224      * Shortcut to do a submit action.
41225      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41226      * @return {BasicForm} this
41227      */
41228     submit : function(options){
41229         this.doAction('submit', options);
41230         return this;
41231     },
41232
41233     /**
41234      * Shortcut to do a load action.
41235      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41236      * @return {BasicForm} this
41237      */
41238     load : function(options){
41239         this.doAction('load', options);
41240         return this;
41241     },
41242
41243     /**
41244      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41245      * @param {Record} record The record to edit
41246      * @return {BasicForm} this
41247      */
41248     updateRecord : function(record){
41249         record.beginEdit();
41250         var fs = record.fields;
41251         fs.each(function(f){
41252             var field = this.findField(f.name);
41253             if(field){
41254                 record.set(f.name, field.getValue());
41255             }
41256         }, this);
41257         record.endEdit();
41258         return this;
41259     },
41260
41261     /**
41262      * Loads an Roo.data.Record into this form.
41263      * @param {Record} record The record to load
41264      * @return {BasicForm} this
41265      */
41266     loadRecord : function(record){
41267         this.setValues(record.data);
41268         return this;
41269     },
41270
41271     // private
41272     beforeAction : function(action){
41273         var o = action.options;
41274         
41275        
41276         if(this.waitMsgTarget === true){
41277             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41278         }else if(this.waitMsgTarget){
41279             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41280             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41281         }else {
41282             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41283         }
41284          
41285     },
41286
41287     // private
41288     afterAction : function(action, success){
41289         this.activeAction = null;
41290         var o = action.options;
41291         
41292         if(this.waitMsgTarget === true){
41293             this.el.unmask();
41294         }else if(this.waitMsgTarget){
41295             this.waitMsgTarget.unmask();
41296         }else{
41297             Roo.MessageBox.updateProgress(1);
41298             Roo.MessageBox.hide();
41299         }
41300          
41301         if(success){
41302             if(o.reset){
41303                 this.reset();
41304             }
41305             Roo.callback(o.success, o.scope, [this, action]);
41306             this.fireEvent('actioncomplete', this, action);
41307             
41308         }else{
41309             Roo.callback(o.failure, o.scope, [this, action]);
41310             // show an error message if no failed handler is set..
41311             if (!this.hasListener('actionfailed')) {
41312                 Roo.MessageBox.alert("Error",
41313                     typeof(action.result.errorMsg) != 'undefined' ?
41314                         action.result.errorMsg :
41315                         "Saving Failed, please check your entries"
41316                 );
41317             }
41318             
41319             this.fireEvent('actionfailed', this, action);
41320         }
41321         
41322     },
41323
41324     /**
41325      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41326      * @param {String} id The value to search for
41327      * @return Field
41328      */
41329     findField : function(id){
41330         var field = this.items.get(id);
41331         if(!field){
41332             this.items.each(function(f){
41333                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41334                     field = f;
41335                     return false;
41336                 }
41337             });
41338         }
41339         return field || null;
41340     },
41341
41342     /**
41343      * Add a secondary form to this one, 
41344      * Used to provide tabbed forms. One form is primary, with hidden values 
41345      * which mirror the elements from the other forms.
41346      * 
41347      * @param {Roo.form.Form} form to add.
41348      * 
41349      */
41350     addForm : function(form)
41351     {
41352        
41353         if (this.childForms.indexOf(form) > -1) {
41354             // already added..
41355             return;
41356         }
41357         this.childForms.push(form);
41358         var n = '';
41359         Roo.each(form.allItems, function (fe) {
41360             
41361             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41362             if (this.findField(n)) { // already added..
41363                 return;
41364             }
41365             var add = new Roo.form.Hidden({
41366                 name : n
41367             });
41368             add.render(this.el);
41369             
41370             this.add( add );
41371         }, this);
41372         
41373     },
41374     /**
41375      * Mark fields in this form invalid in bulk.
41376      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41377      * @return {BasicForm} this
41378      */
41379     markInvalid : function(errors){
41380         if(errors instanceof Array){
41381             for(var i = 0, len = errors.length; i < len; i++){
41382                 var fieldError = errors[i];
41383                 var f = this.findField(fieldError.id);
41384                 if(f){
41385                     f.markInvalid(fieldError.msg);
41386                 }
41387             }
41388         }else{
41389             var field, id;
41390             for(id in errors){
41391                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41392                     field.markInvalid(errors[id]);
41393                 }
41394             }
41395         }
41396         Roo.each(this.childForms || [], function (f) {
41397             f.markInvalid(errors);
41398         });
41399         
41400         return this;
41401     },
41402
41403     /**
41404      * Set values for fields in this form in bulk.
41405      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41406      * @return {BasicForm} this
41407      */
41408     setValues : function(values){
41409         if(values instanceof Array){ // array of objects
41410             for(var i = 0, len = values.length; i < len; i++){
41411                 var v = values[i];
41412                 var f = this.findField(v.id);
41413                 if(f){
41414                     f.setValue(v.value);
41415                     if(this.trackResetOnLoad){
41416                         f.originalValue = f.getValue();
41417                     }
41418                 }
41419             }
41420         }else{ // object hash
41421             var field, id;
41422             for(id in values){
41423                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41424                     
41425                     if (field.setFromData && 
41426                         field.valueField && 
41427                         field.displayField &&
41428                         // combos' with local stores can 
41429                         // be queried via setValue()
41430                         // to set their value..
41431                         (field.store && !field.store.isLocal)
41432                         ) {
41433                         // it's a combo
41434                         var sd = { };
41435                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41436                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41437                         field.setFromData(sd);
41438                         
41439                     } else {
41440                         field.setValue(values[id]);
41441                     }
41442                     
41443                     
41444                     if(this.trackResetOnLoad){
41445                         field.originalValue = field.getValue();
41446                     }
41447                 }
41448             }
41449         }
41450          
41451         Roo.each(this.childForms || [], function (f) {
41452             f.setValues(values);
41453         });
41454                 
41455         return this;
41456     },
41457
41458     /**
41459      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41460      * they are returned as an array.
41461      * @param {Boolean} asString
41462      * @return {Object}
41463      */
41464     getValues : function(asString){
41465         if (this.childForms) {
41466             // copy values from the child forms
41467             Roo.each(this.childForms, function (f) {
41468                 this.setValues(f.getValues());
41469             }, this);
41470         }
41471         
41472         
41473         
41474         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41475         if(asString === true){
41476             return fs;
41477         }
41478         return Roo.urlDecode(fs);
41479     },
41480     
41481     /**
41482      * Returns the fields in this form as an object with key/value pairs. 
41483      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41484      * @return {Object}
41485      */
41486     getFieldValues : function(with_hidden)
41487     {
41488         if (this.childForms) {
41489             // copy values from the child forms
41490             // should this call getFieldValues - probably not as we do not currently copy
41491             // hidden fields when we generate..
41492             Roo.each(this.childForms, function (f) {
41493                 this.setValues(f.getValues());
41494             }, this);
41495         }
41496         
41497         var ret = {};
41498         this.items.each(function(f){
41499             if (!f.getName()) {
41500                 return;
41501             }
41502             var v = f.getValue();
41503             // not sure if this supported any more..
41504             if ((typeof(v) == 'object') && f.getRawValue) {
41505                 v = f.getRawValue() ; // dates..
41506             }
41507             // combo boxes where name != hiddenName...
41508             if (f.name != f.getName()) {
41509                 ret[f.name] = f.getRawValue();
41510             }
41511             ret[f.getName()] = v;
41512         });
41513         
41514         return ret;
41515     },
41516
41517     /**
41518      * Clears all invalid messages in this form.
41519      * @return {BasicForm} this
41520      */
41521     clearInvalid : function(){
41522         this.items.each(function(f){
41523            f.clearInvalid();
41524         });
41525         
41526         Roo.each(this.childForms || [], function (f) {
41527             f.clearInvalid();
41528         });
41529         
41530         
41531         return this;
41532     },
41533
41534     /**
41535      * Resets this form.
41536      * @return {BasicForm} this
41537      */
41538     reset : function(){
41539         this.items.each(function(f){
41540             f.reset();
41541         });
41542         
41543         Roo.each(this.childForms || [], function (f) {
41544             f.reset();
41545         });
41546        
41547         
41548         return this;
41549     },
41550
41551     /**
41552      * Add Roo.form components to this form.
41553      * @param {Field} field1
41554      * @param {Field} field2 (optional)
41555      * @param {Field} etc (optional)
41556      * @return {BasicForm} this
41557      */
41558     add : function(){
41559         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41560         return this;
41561     },
41562
41563
41564     /**
41565      * Removes a field from the items collection (does NOT remove its markup).
41566      * @param {Field} field
41567      * @return {BasicForm} this
41568      */
41569     remove : function(field){
41570         this.items.remove(field);
41571         return this;
41572     },
41573
41574     /**
41575      * Looks at the fields in this form, checks them for an id attribute,
41576      * and calls applyTo on the existing dom element with that id.
41577      * @return {BasicForm} this
41578      */
41579     render : function(){
41580         this.items.each(function(f){
41581             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41582                 f.applyTo(f.id);
41583             }
41584         });
41585         return this;
41586     },
41587
41588     /**
41589      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41590      * @param {Object} values
41591      * @return {BasicForm} this
41592      */
41593     applyToFields : function(o){
41594         this.items.each(function(f){
41595            Roo.apply(f, o);
41596         });
41597         return this;
41598     },
41599
41600     /**
41601      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41602      * @param {Object} values
41603      * @return {BasicForm} this
41604      */
41605     applyIfToFields : function(o){
41606         this.items.each(function(f){
41607            Roo.applyIf(f, o);
41608         });
41609         return this;
41610     }
41611 });
41612
41613 // back compat
41614 Roo.BasicForm = Roo.form.BasicForm;/*
41615  * Based on:
41616  * Ext JS Library 1.1.1
41617  * Copyright(c) 2006-2007, Ext JS, LLC.
41618  *
41619  * Originally Released Under LGPL - original licence link has changed is not relivant.
41620  *
41621  * Fork - LGPL
41622  * <script type="text/javascript">
41623  */
41624
41625 /**
41626  * @class Roo.form.Form
41627  * @extends Roo.form.BasicForm
41628  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41629  * @constructor
41630  * @param {Object} config Configuration options
41631  */
41632 Roo.form.Form = function(config){
41633     var xitems =  [];
41634     if (config.items) {
41635         xitems = config.items;
41636         delete config.items;
41637     }
41638    
41639     
41640     Roo.form.Form.superclass.constructor.call(this, null, config);
41641     this.url = this.url || this.action;
41642     if(!this.root){
41643         this.root = new Roo.form.Layout(Roo.applyIf({
41644             id: Roo.id()
41645         }, config));
41646     }
41647     this.active = this.root;
41648     /**
41649      * Array of all the buttons that have been added to this form via {@link addButton}
41650      * @type Array
41651      */
41652     this.buttons = [];
41653     this.allItems = [];
41654     this.addEvents({
41655         /**
41656          * @event clientvalidation
41657          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41658          * @param {Form} this
41659          * @param {Boolean} valid true if the form has passed client-side validation
41660          */
41661         clientvalidation: true,
41662         /**
41663          * @event rendered
41664          * Fires when the form is rendered
41665          * @param {Roo.form.Form} form
41666          */
41667         rendered : true
41668     });
41669     
41670     if (this.progressUrl) {
41671             // push a hidden field onto the list of fields..
41672             this.addxtype( {
41673                     xns: Roo.form, 
41674                     xtype : 'Hidden', 
41675                     name : 'UPLOAD_IDENTIFIER' 
41676             });
41677         }
41678         
41679     
41680     Roo.each(xitems, this.addxtype, this);
41681     
41682     
41683     
41684 };
41685
41686 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41687     /**
41688      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41689      */
41690     /**
41691      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41692      */
41693     /**
41694      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41695      */
41696     buttonAlign:'center',
41697
41698     /**
41699      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41700      */
41701     minButtonWidth:75,
41702
41703     /**
41704      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41705      * This property cascades to child containers if not set.
41706      */
41707     labelAlign:'left',
41708
41709     /**
41710      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41711      * fires a looping event with that state. This is required to bind buttons to the valid
41712      * state using the config value formBind:true on the button.
41713      */
41714     monitorValid : false,
41715
41716     /**
41717      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41718      */
41719     monitorPoll : 200,
41720     
41721     /**
41722      * @cfg {String} progressUrl - Url to return progress data 
41723      */
41724     
41725     progressUrl : false,
41726   
41727     /**
41728      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41729      * fields are added and the column is closed. If no fields are passed the column remains open
41730      * until end() is called.
41731      * @param {Object} config The config to pass to the column
41732      * @param {Field} field1 (optional)
41733      * @param {Field} field2 (optional)
41734      * @param {Field} etc (optional)
41735      * @return Column The column container object
41736      */
41737     column : function(c){
41738         var col = new Roo.form.Column(c);
41739         this.start(col);
41740         if(arguments.length > 1){ // duplicate code required because of Opera
41741             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41742             this.end();
41743         }
41744         return col;
41745     },
41746
41747     /**
41748      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41749      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41750      * until end() is called.
41751      * @param {Object} config The config to pass to the fieldset
41752      * @param {Field} field1 (optional)
41753      * @param {Field} field2 (optional)
41754      * @param {Field} etc (optional)
41755      * @return FieldSet The fieldset container object
41756      */
41757     fieldset : function(c){
41758         var fs = new Roo.form.FieldSet(c);
41759         this.start(fs);
41760         if(arguments.length > 1){ // duplicate code required because of Opera
41761             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41762             this.end();
41763         }
41764         return fs;
41765     },
41766
41767     /**
41768      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41769      * fields are added and the container is closed. If no fields are passed the container remains open
41770      * until end() is called.
41771      * @param {Object} config The config to pass to the Layout
41772      * @param {Field} field1 (optional)
41773      * @param {Field} field2 (optional)
41774      * @param {Field} etc (optional)
41775      * @return Layout The container object
41776      */
41777     container : function(c){
41778         var l = new Roo.form.Layout(c);
41779         this.start(l);
41780         if(arguments.length > 1){ // duplicate code required because of Opera
41781             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41782             this.end();
41783         }
41784         return l;
41785     },
41786
41787     /**
41788      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41789      * @param {Object} container A Roo.form.Layout or subclass of Layout
41790      * @return {Form} this
41791      */
41792     start : function(c){
41793         // cascade label info
41794         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41795         this.active.stack.push(c);
41796         c.ownerCt = this.active;
41797         this.active = c;
41798         return this;
41799     },
41800
41801     /**
41802      * Closes the current open container
41803      * @return {Form} this
41804      */
41805     end : function(){
41806         if(this.active == this.root){
41807             return this;
41808         }
41809         this.active = this.active.ownerCt;
41810         return this;
41811     },
41812
41813     /**
41814      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41815      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41816      * as the label of the field.
41817      * @param {Field} field1
41818      * @param {Field} field2 (optional)
41819      * @param {Field} etc. (optional)
41820      * @return {Form} this
41821      */
41822     add : function(){
41823         this.active.stack.push.apply(this.active.stack, arguments);
41824         this.allItems.push.apply(this.allItems,arguments);
41825         var r = [];
41826         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41827             if(a[i].isFormField){
41828                 r.push(a[i]);
41829             }
41830         }
41831         if(r.length > 0){
41832             Roo.form.Form.superclass.add.apply(this, r);
41833         }
41834         return this;
41835     },
41836     
41837
41838     
41839     
41840     
41841      /**
41842      * Find any element that has been added to a form, using it's ID or name
41843      * This can include framesets, columns etc. along with regular fields..
41844      * @param {String} id - id or name to find.
41845      
41846      * @return {Element} e - or false if nothing found.
41847      */
41848     findbyId : function(id)
41849     {
41850         var ret = false;
41851         if (!id) {
41852             return ret;
41853         }
41854         Roo.each(this.allItems, function(f){
41855             if (f.id == id || f.name == id ){
41856                 ret = f;
41857                 return false;
41858             }
41859         });
41860         return ret;
41861     },
41862
41863     
41864     
41865     /**
41866      * Render this form into the passed container. This should only be called once!
41867      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41868      * @return {Form} this
41869      */
41870     render : function(ct)
41871     {
41872         
41873         
41874         
41875         ct = Roo.get(ct);
41876         var o = this.autoCreate || {
41877             tag: 'form',
41878             method : this.method || 'POST',
41879             id : this.id || Roo.id()
41880         };
41881         this.initEl(ct.createChild(o));
41882
41883         this.root.render(this.el);
41884         
41885        
41886              
41887         this.items.each(function(f){
41888             f.render('x-form-el-'+f.id);
41889         });
41890
41891         if(this.buttons.length > 0){
41892             // tables are required to maintain order and for correct IE layout
41893             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41894                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41895                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41896             }}, null, true);
41897             var tr = tb.getElementsByTagName('tr')[0];
41898             for(var i = 0, len = this.buttons.length; i < len; i++) {
41899                 var b = this.buttons[i];
41900                 var td = document.createElement('td');
41901                 td.className = 'x-form-btn-td';
41902                 b.render(tr.appendChild(td));
41903             }
41904         }
41905         if(this.monitorValid){ // initialize after render
41906             this.startMonitoring();
41907         }
41908         this.fireEvent('rendered', this);
41909         return this;
41910     },
41911
41912     /**
41913      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41914      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41915      * object or a valid Roo.DomHelper element config
41916      * @param {Function} handler The function called when the button is clicked
41917      * @param {Object} scope (optional) The scope of the handler function
41918      * @return {Roo.Button}
41919      */
41920     addButton : function(config, handler, scope){
41921         var bc = {
41922             handler: handler,
41923             scope: scope,
41924             minWidth: this.minButtonWidth,
41925             hideParent:true
41926         };
41927         if(typeof config == "string"){
41928             bc.text = config;
41929         }else{
41930             Roo.apply(bc, config);
41931         }
41932         var btn = new Roo.Button(null, bc);
41933         this.buttons.push(btn);
41934         return btn;
41935     },
41936
41937      /**
41938      * Adds a series of form elements (using the xtype property as the factory method.
41939      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41940      * @param {Object} config 
41941      */
41942     
41943     addxtype : function()
41944     {
41945         var ar = Array.prototype.slice.call(arguments, 0);
41946         var ret = false;
41947         for(var i = 0; i < ar.length; i++) {
41948             if (!ar[i]) {
41949                 continue; // skip -- if this happends something invalid got sent, we 
41950                 // should ignore it, as basically that interface element will not show up
41951                 // and that should be pretty obvious!!
41952             }
41953             
41954             if (Roo.form[ar[i].xtype]) {
41955                 ar[i].form = this;
41956                 var fe = Roo.factory(ar[i], Roo.form);
41957                 if (!ret) {
41958                     ret = fe;
41959                 }
41960                 fe.form = this;
41961                 if (fe.store) {
41962                     fe.store.form = this;
41963                 }
41964                 if (fe.isLayout) {  
41965                          
41966                     this.start(fe);
41967                     this.allItems.push(fe);
41968                     if (fe.items && fe.addxtype) {
41969                         fe.addxtype.apply(fe, fe.items);
41970                         delete fe.items;
41971                     }
41972                      this.end();
41973                     continue;
41974                 }
41975                 
41976                 
41977                  
41978                 this.add(fe);
41979               //  console.log('adding ' + ar[i].xtype);
41980             }
41981             if (ar[i].xtype == 'Button') {  
41982                 //console.log('adding button');
41983                 //console.log(ar[i]);
41984                 this.addButton(ar[i]);
41985                 this.allItems.push(fe);
41986                 continue;
41987             }
41988             
41989             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41990                 alert('end is not supported on xtype any more, use items');
41991             //    this.end();
41992             //    //console.log('adding end');
41993             }
41994             
41995         }
41996         return ret;
41997     },
41998     
41999     /**
42000      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
42001      * option "monitorValid"
42002      */
42003     startMonitoring : function(){
42004         if(!this.bound){
42005             this.bound = true;
42006             Roo.TaskMgr.start({
42007                 run : this.bindHandler,
42008                 interval : this.monitorPoll || 200,
42009                 scope: this
42010             });
42011         }
42012     },
42013
42014     /**
42015      * Stops monitoring of the valid state of this form
42016      */
42017     stopMonitoring : function(){
42018         this.bound = false;
42019     },
42020
42021     // private
42022     bindHandler : function(){
42023         if(!this.bound){
42024             return false; // stops binding
42025         }
42026         var valid = true;
42027         this.items.each(function(f){
42028             if(!f.isValid(true)){
42029                 valid = false;
42030                 return false;
42031             }
42032         });
42033         for(var i = 0, len = this.buttons.length; i < len; i++){
42034             var btn = this.buttons[i];
42035             if(btn.formBind === true && btn.disabled === valid){
42036                 btn.setDisabled(!valid);
42037             }
42038         }
42039         this.fireEvent('clientvalidation', this, valid);
42040     }
42041     
42042     
42043     
42044     
42045     
42046     
42047     
42048     
42049 });
42050
42051
42052 // back compat
42053 Roo.Form = Roo.form.Form;
42054 /*
42055  * Based on:
42056  * Ext JS Library 1.1.1
42057  * Copyright(c) 2006-2007, Ext JS, LLC.
42058  *
42059  * Originally Released Under LGPL - original licence link has changed is not relivant.
42060  *
42061  * Fork - LGPL
42062  * <script type="text/javascript">
42063  */
42064  
42065  /**
42066  * @class Roo.form.Action
42067  * Internal Class used to handle form actions
42068  * @constructor
42069  * @param {Roo.form.BasicForm} el The form element or its id
42070  * @param {Object} config Configuration options
42071  */
42072  
42073  
42074 // define the action interface
42075 Roo.form.Action = function(form, options){
42076     this.form = form;
42077     this.options = options || {};
42078 };
42079 /**
42080  * Client Validation Failed
42081  * @const 
42082  */
42083 Roo.form.Action.CLIENT_INVALID = 'client';
42084 /**
42085  * Server Validation Failed
42086  * @const 
42087  */
42088  Roo.form.Action.SERVER_INVALID = 'server';
42089  /**
42090  * Connect to Server Failed
42091  * @const 
42092  */
42093 Roo.form.Action.CONNECT_FAILURE = 'connect';
42094 /**
42095  * Reading Data from Server Failed
42096  * @const 
42097  */
42098 Roo.form.Action.LOAD_FAILURE = 'load';
42099
42100 Roo.form.Action.prototype = {
42101     type : 'default',
42102     failureType : undefined,
42103     response : undefined,
42104     result : undefined,
42105
42106     // interface method
42107     run : function(options){
42108
42109     },
42110
42111     // interface method
42112     success : function(response){
42113
42114     },
42115
42116     // interface method
42117     handleResponse : function(response){
42118
42119     },
42120
42121     // default connection failure
42122     failure : function(response){
42123         
42124         this.response = response;
42125         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42126         this.form.afterAction(this, false);
42127     },
42128
42129     processResponse : function(response){
42130         this.response = response;
42131         if(!response.responseText){
42132             return true;
42133         }
42134         this.result = this.handleResponse(response);
42135         return this.result;
42136     },
42137
42138     // utility functions used internally
42139     getUrl : function(appendParams){
42140         var url = this.options.url || this.form.url || this.form.el.dom.action;
42141         if(appendParams){
42142             var p = this.getParams();
42143             if(p){
42144                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42145             }
42146         }
42147         return url;
42148     },
42149
42150     getMethod : function(){
42151         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42152     },
42153
42154     getParams : function(){
42155         var bp = this.form.baseParams;
42156         var p = this.options.params;
42157         if(p){
42158             if(typeof p == "object"){
42159                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42160             }else if(typeof p == 'string' && bp){
42161                 p += '&' + Roo.urlEncode(bp);
42162             }
42163         }else if(bp){
42164             p = Roo.urlEncode(bp);
42165         }
42166         return p;
42167     },
42168
42169     createCallback : function(){
42170         return {
42171             success: this.success,
42172             failure: this.failure,
42173             scope: this,
42174             timeout: (this.form.timeout*1000),
42175             upload: this.form.fileUpload ? this.success : undefined
42176         };
42177     }
42178 };
42179
42180 Roo.form.Action.Submit = function(form, options){
42181     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42182 };
42183
42184 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42185     type : 'submit',
42186
42187     haveProgress : false,
42188     uploadComplete : false,
42189     
42190     // uploadProgress indicator.
42191     uploadProgress : function()
42192     {
42193         if (!this.form.progressUrl) {
42194             return;
42195         }
42196         
42197         if (!this.haveProgress) {
42198             Roo.MessageBox.progress("Uploading", "Uploading");
42199         }
42200         if (this.uploadComplete) {
42201            Roo.MessageBox.hide();
42202            return;
42203         }
42204         
42205         this.haveProgress = true;
42206    
42207         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42208         
42209         var c = new Roo.data.Connection();
42210         c.request({
42211             url : this.form.progressUrl,
42212             params: {
42213                 id : uid
42214             },
42215             method: 'GET',
42216             success : function(req){
42217                //console.log(data);
42218                 var rdata = false;
42219                 var edata;
42220                 try  {
42221                    rdata = Roo.decode(req.responseText)
42222                 } catch (e) {
42223                     Roo.log("Invalid data from server..");
42224                     Roo.log(edata);
42225                     return;
42226                 }
42227                 if (!rdata || !rdata.success) {
42228                     Roo.log(rdata);
42229                     return;
42230                 }
42231                 var data = rdata.data;
42232                 
42233                 if (this.uploadComplete) {
42234                    Roo.MessageBox.hide();
42235                    return;
42236                 }
42237                    
42238                 if (data){
42239                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42240                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42241                     );
42242                 }
42243                 this.uploadProgress.defer(2000,this);
42244             },
42245        
42246             failure: function(data) {
42247                 Roo.log('progress url failed ');
42248                 Roo.log(data);
42249             },
42250             scope : this
42251         });
42252            
42253     },
42254     
42255     
42256     run : function()
42257     {
42258         // run get Values on the form, so it syncs any secondary forms.
42259         this.form.getValues();
42260         
42261         var o = this.options;
42262         var method = this.getMethod();
42263         var isPost = method == 'POST';
42264         if(o.clientValidation === false || this.form.isValid()){
42265             
42266             if (this.form.progressUrl) {
42267                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42268                     (new Date() * 1) + '' + Math.random());
42269                     
42270             } 
42271             
42272             
42273             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42274                 form:this.form.el.dom,
42275                 url:this.getUrl(!isPost),
42276                 method: method,
42277                 params:isPost ? this.getParams() : null,
42278                 isUpload: this.form.fileUpload
42279             }));
42280             
42281             this.uploadProgress();
42282
42283         }else if (o.clientValidation !== false){ // client validation failed
42284             this.failureType = Roo.form.Action.CLIENT_INVALID;
42285             this.form.afterAction(this, false);
42286         }
42287     },
42288
42289     success : function(response)
42290     {
42291         this.uploadComplete= true;
42292         if (this.haveProgress) {
42293             Roo.MessageBox.hide();
42294         }
42295         
42296         
42297         var result = this.processResponse(response);
42298         if(result === true || result.success){
42299             this.form.afterAction(this, true);
42300             return;
42301         }
42302         if(result.errors){
42303             this.form.markInvalid(result.errors);
42304             this.failureType = Roo.form.Action.SERVER_INVALID;
42305         }
42306         this.form.afterAction(this, false);
42307     },
42308     failure : function(response)
42309     {
42310         this.uploadComplete= true;
42311         if (this.haveProgress) {
42312             Roo.MessageBox.hide();
42313         }
42314         
42315         this.response = response;
42316         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42317         this.form.afterAction(this, false);
42318     },
42319     
42320     handleResponse : function(response){
42321         if(this.form.errorReader){
42322             var rs = this.form.errorReader.read(response);
42323             var errors = [];
42324             if(rs.records){
42325                 for(var i = 0, len = rs.records.length; i < len; i++) {
42326                     var r = rs.records[i];
42327                     errors[i] = r.data;
42328                 }
42329             }
42330             if(errors.length < 1){
42331                 errors = null;
42332             }
42333             return {
42334                 success : rs.success,
42335                 errors : errors
42336             };
42337         }
42338         var ret = false;
42339         try {
42340             ret = Roo.decode(response.responseText);
42341         } catch (e) {
42342             ret = {
42343                 success: false,
42344                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42345                 errors : []
42346             };
42347         }
42348         return ret;
42349         
42350     }
42351 });
42352
42353
42354 Roo.form.Action.Load = function(form, options){
42355     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42356     this.reader = this.form.reader;
42357 };
42358
42359 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42360     type : 'load',
42361
42362     run : function(){
42363         
42364         Roo.Ajax.request(Roo.apply(
42365                 this.createCallback(), {
42366                     method:this.getMethod(),
42367                     url:this.getUrl(false),
42368                     params:this.getParams()
42369         }));
42370     },
42371
42372     success : function(response){
42373         
42374         var result = this.processResponse(response);
42375         if(result === true || !result.success || !result.data){
42376             this.failureType = Roo.form.Action.LOAD_FAILURE;
42377             this.form.afterAction(this, false);
42378             return;
42379         }
42380         this.form.clearInvalid();
42381         this.form.setValues(result.data);
42382         this.form.afterAction(this, true);
42383     },
42384
42385     handleResponse : function(response){
42386         if(this.form.reader){
42387             var rs = this.form.reader.read(response);
42388             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42389             return {
42390                 success : rs.success,
42391                 data : data
42392             };
42393         }
42394         return Roo.decode(response.responseText);
42395     }
42396 });
42397
42398 Roo.form.Action.ACTION_TYPES = {
42399     'load' : Roo.form.Action.Load,
42400     'submit' : Roo.form.Action.Submit
42401 };/*
42402  * Based on:
42403  * Ext JS Library 1.1.1
42404  * Copyright(c) 2006-2007, Ext JS, LLC.
42405  *
42406  * Originally Released Under LGPL - original licence link has changed is not relivant.
42407  *
42408  * Fork - LGPL
42409  * <script type="text/javascript">
42410  */
42411  
42412 /**
42413  * @class Roo.form.Layout
42414  * @extends Roo.Component
42415  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42416  * @constructor
42417  * @param {Object} config Configuration options
42418  */
42419 Roo.form.Layout = function(config){
42420     var xitems = [];
42421     if (config.items) {
42422         xitems = config.items;
42423         delete config.items;
42424     }
42425     Roo.form.Layout.superclass.constructor.call(this, config);
42426     this.stack = [];
42427     Roo.each(xitems, this.addxtype, this);
42428      
42429 };
42430
42431 Roo.extend(Roo.form.Layout, Roo.Component, {
42432     /**
42433      * @cfg {String/Object} autoCreate
42434      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42435      */
42436     /**
42437      * @cfg {String/Object/Function} style
42438      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42439      * a function which returns such a specification.
42440      */
42441     /**
42442      * @cfg {String} labelAlign
42443      * Valid values are "left," "top" and "right" (defaults to "left")
42444      */
42445     /**
42446      * @cfg {Number} labelWidth
42447      * Fixed width in pixels of all field labels (defaults to undefined)
42448      */
42449     /**
42450      * @cfg {Boolean} clear
42451      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42452      */
42453     clear : true,
42454     /**
42455      * @cfg {String} labelSeparator
42456      * The separator to use after field labels (defaults to ':')
42457      */
42458     labelSeparator : ':',
42459     /**
42460      * @cfg {Boolean} hideLabels
42461      * True to suppress the display of field labels in this layout (defaults to false)
42462      */
42463     hideLabels : false,
42464
42465     // private
42466     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42467     
42468     isLayout : true,
42469     
42470     // private
42471     onRender : function(ct, position){
42472         if(this.el){ // from markup
42473             this.el = Roo.get(this.el);
42474         }else {  // generate
42475             var cfg = this.getAutoCreate();
42476             this.el = ct.createChild(cfg, position);
42477         }
42478         if(this.style){
42479             this.el.applyStyles(this.style);
42480         }
42481         if(this.labelAlign){
42482             this.el.addClass('x-form-label-'+this.labelAlign);
42483         }
42484         if(this.hideLabels){
42485             this.labelStyle = "display:none";
42486             this.elementStyle = "padding-left:0;";
42487         }else{
42488             if(typeof this.labelWidth == 'number'){
42489                 this.labelStyle = "width:"+this.labelWidth+"px;";
42490                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42491             }
42492             if(this.labelAlign == 'top'){
42493                 this.labelStyle = "width:auto;";
42494                 this.elementStyle = "padding-left:0;";
42495             }
42496         }
42497         var stack = this.stack;
42498         var slen = stack.length;
42499         if(slen > 0){
42500             if(!this.fieldTpl){
42501                 var t = new Roo.Template(
42502                     '<div class="x-form-item {5}">',
42503                         '<label for="{0}" style="{2}">{1}{4}</label>',
42504                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42505                         '</div>',
42506                     '</div><div class="x-form-clear-left"></div>'
42507                 );
42508                 t.disableFormats = true;
42509                 t.compile();
42510                 Roo.form.Layout.prototype.fieldTpl = t;
42511             }
42512             for(var i = 0; i < slen; i++) {
42513                 if(stack[i].isFormField){
42514                     this.renderField(stack[i]);
42515                 }else{
42516                     this.renderComponent(stack[i]);
42517                 }
42518             }
42519         }
42520         if(this.clear){
42521             this.el.createChild({cls:'x-form-clear'});
42522         }
42523     },
42524
42525     // private
42526     renderField : function(f){
42527         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42528                f.id, //0
42529                f.fieldLabel, //1
42530                f.labelStyle||this.labelStyle||'', //2
42531                this.elementStyle||'', //3
42532                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42533                f.itemCls||this.itemCls||''  //5
42534        ], true).getPrevSibling());
42535     },
42536
42537     // private
42538     renderComponent : function(c){
42539         c.render(c.isLayout ? this.el : this.el.createChild());    
42540     },
42541     /**
42542      * Adds a object form elements (using the xtype property as the factory method.)
42543      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42544      * @param {Object} config 
42545      */
42546     addxtype : function(o)
42547     {
42548         // create the lement.
42549         o.form = this.form;
42550         var fe = Roo.factory(o, Roo.form);
42551         this.form.allItems.push(fe);
42552         this.stack.push(fe);
42553         
42554         if (fe.isFormField) {
42555             this.form.items.add(fe);
42556         }
42557          
42558         return fe;
42559     }
42560 });
42561
42562 /**
42563  * @class Roo.form.Column
42564  * @extends Roo.form.Layout
42565  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42566  * @constructor
42567  * @param {Object} config Configuration options
42568  */
42569 Roo.form.Column = function(config){
42570     Roo.form.Column.superclass.constructor.call(this, config);
42571 };
42572
42573 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42574     /**
42575      * @cfg {Number/String} width
42576      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42577      */
42578     /**
42579      * @cfg {String/Object} autoCreate
42580      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42581      */
42582
42583     // private
42584     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42585
42586     // private
42587     onRender : function(ct, position){
42588         Roo.form.Column.superclass.onRender.call(this, ct, position);
42589         if(this.width){
42590             this.el.setWidth(this.width);
42591         }
42592     }
42593 });
42594
42595
42596 /**
42597  * @class Roo.form.Row
42598  * @extends Roo.form.Layout
42599  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42600  * @constructor
42601  * @param {Object} config Configuration options
42602  */
42603
42604  
42605 Roo.form.Row = function(config){
42606     Roo.form.Row.superclass.constructor.call(this, config);
42607 };
42608  
42609 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42610       /**
42611      * @cfg {Number/String} width
42612      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42613      */
42614     /**
42615      * @cfg {Number/String} height
42616      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42617      */
42618     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42619     
42620     padWidth : 20,
42621     // private
42622     onRender : function(ct, position){
42623         //console.log('row render');
42624         if(!this.rowTpl){
42625             var t = new Roo.Template(
42626                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42627                     '<label for="{0}" style="{2}">{1}{4}</label>',
42628                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42629                     '</div>',
42630                 '</div>'
42631             );
42632             t.disableFormats = true;
42633             t.compile();
42634             Roo.form.Layout.prototype.rowTpl = t;
42635         }
42636         this.fieldTpl = this.rowTpl;
42637         
42638         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42639         var labelWidth = 100;
42640         
42641         if ((this.labelAlign != 'top')) {
42642             if (typeof this.labelWidth == 'number') {
42643                 labelWidth = this.labelWidth
42644             }
42645             this.padWidth =  20 + labelWidth;
42646             
42647         }
42648         
42649         Roo.form.Column.superclass.onRender.call(this, ct, position);
42650         if(this.width){
42651             this.el.setWidth(this.width);
42652         }
42653         if(this.height){
42654             this.el.setHeight(this.height);
42655         }
42656     },
42657     
42658     // private
42659     renderField : function(f){
42660         f.fieldEl = this.fieldTpl.append(this.el, [
42661                f.id, f.fieldLabel,
42662                f.labelStyle||this.labelStyle||'',
42663                this.elementStyle||'',
42664                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42665                f.itemCls||this.itemCls||'',
42666                f.width ? f.width + this.padWidth : 160 + this.padWidth
42667        ],true);
42668     }
42669 });
42670  
42671
42672 /**
42673  * @class Roo.form.FieldSet
42674  * @extends Roo.form.Layout
42675  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42676  * @constructor
42677  * @param {Object} config Configuration options
42678  */
42679 Roo.form.FieldSet = function(config){
42680     Roo.form.FieldSet.superclass.constructor.call(this, config);
42681 };
42682
42683 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42684     /**
42685      * @cfg {String} legend
42686      * The text to display as the legend for the FieldSet (defaults to '')
42687      */
42688     /**
42689      * @cfg {String/Object} autoCreate
42690      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42691      */
42692
42693     // private
42694     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42695
42696     // private
42697     onRender : function(ct, position){
42698         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42699         if(this.legend){
42700             this.setLegend(this.legend);
42701         }
42702     },
42703
42704     // private
42705     setLegend : function(text){
42706         if(this.rendered){
42707             this.el.child('legend').update(text);
42708         }
42709     }
42710 });/*
42711  * Based on:
42712  * Ext JS Library 1.1.1
42713  * Copyright(c) 2006-2007, Ext JS, LLC.
42714  *
42715  * Originally Released Under LGPL - original licence link has changed is not relivant.
42716  *
42717  * Fork - LGPL
42718  * <script type="text/javascript">
42719  */
42720 /**
42721  * @class Roo.form.VTypes
42722  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42723  * @singleton
42724  */
42725 Roo.form.VTypes = function(){
42726     // closure these in so they are only created once.
42727     var alpha = /^[a-zA-Z_]+$/;
42728     var alphanum = /^[a-zA-Z0-9_]+$/;
42729     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42730     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42731
42732     // All these messages and functions are configurable
42733     return {
42734         /**
42735          * The function used to validate email addresses
42736          * @param {String} value The email address
42737          */
42738         'email' : function(v){
42739             return email.test(v);
42740         },
42741         /**
42742          * The error text to display when the email validation function returns false
42743          * @type String
42744          */
42745         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42746         /**
42747          * The keystroke filter mask to be applied on email input
42748          * @type RegExp
42749          */
42750         'emailMask' : /[a-z0-9_\.\-@]/i,
42751
42752         /**
42753          * The function used to validate URLs
42754          * @param {String} value The URL
42755          */
42756         'url' : function(v){
42757             return url.test(v);
42758         },
42759         /**
42760          * The error text to display when the url validation function returns false
42761          * @type String
42762          */
42763         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42764         
42765         /**
42766          * The function used to validate alpha values
42767          * @param {String} value The value
42768          */
42769         'alpha' : function(v){
42770             return alpha.test(v);
42771         },
42772         /**
42773          * The error text to display when the alpha validation function returns false
42774          * @type String
42775          */
42776         'alphaText' : 'This field should only contain letters and _',
42777         /**
42778          * The keystroke filter mask to be applied on alpha input
42779          * @type RegExp
42780          */
42781         'alphaMask' : /[a-z_]/i,
42782
42783         /**
42784          * The function used to validate alphanumeric values
42785          * @param {String} value The value
42786          */
42787         'alphanum' : function(v){
42788             return alphanum.test(v);
42789         },
42790         /**
42791          * The error text to display when the alphanumeric validation function returns false
42792          * @type String
42793          */
42794         'alphanumText' : 'This field should only contain letters, numbers and _',
42795         /**
42796          * The keystroke filter mask to be applied on alphanumeric input
42797          * @type RegExp
42798          */
42799         'alphanumMask' : /[a-z0-9_]/i
42800     };
42801 }();//<script type="text/javascript">
42802
42803 /**
42804  * @class Roo.form.FCKeditor
42805  * @extends Roo.form.TextArea
42806  * Wrapper around the FCKEditor http://www.fckeditor.net
42807  * @constructor
42808  * Creates a new FCKeditor
42809  * @param {Object} config Configuration options
42810  */
42811 Roo.form.FCKeditor = function(config){
42812     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42813     this.addEvents({
42814          /**
42815          * @event editorinit
42816          * Fired when the editor is initialized - you can add extra handlers here..
42817          * @param {FCKeditor} this
42818          * @param {Object} the FCK object.
42819          */
42820         editorinit : true
42821     });
42822     
42823     
42824 };
42825 Roo.form.FCKeditor.editors = { };
42826 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42827 {
42828     //defaultAutoCreate : {
42829     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42830     //},
42831     // private
42832     /**
42833      * @cfg {Object} fck options - see fck manual for details.
42834      */
42835     fckconfig : false,
42836     
42837     /**
42838      * @cfg {Object} fck toolbar set (Basic or Default)
42839      */
42840     toolbarSet : 'Basic',
42841     /**
42842      * @cfg {Object} fck BasePath
42843      */ 
42844     basePath : '/fckeditor/',
42845     
42846     
42847     frame : false,
42848     
42849     value : '',
42850     
42851    
42852     onRender : function(ct, position)
42853     {
42854         if(!this.el){
42855             this.defaultAutoCreate = {
42856                 tag: "textarea",
42857                 style:"width:300px;height:60px;",
42858                 autocomplete: "off"
42859             };
42860         }
42861         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42862         /*
42863         if(this.grow){
42864             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42865             if(this.preventScrollbars){
42866                 this.el.setStyle("overflow", "hidden");
42867             }
42868             this.el.setHeight(this.growMin);
42869         }
42870         */
42871         //console.log('onrender' + this.getId() );
42872         Roo.form.FCKeditor.editors[this.getId()] = this;
42873          
42874
42875         this.replaceTextarea() ;
42876         
42877     },
42878     
42879     getEditor : function() {
42880         return this.fckEditor;
42881     },
42882     /**
42883      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42884      * @param {Mixed} value The value to set
42885      */
42886     
42887     
42888     setValue : function(value)
42889     {
42890         //console.log('setValue: ' + value);
42891         
42892         if(typeof(value) == 'undefined') { // not sure why this is happending...
42893             return;
42894         }
42895         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42896         
42897         //if(!this.el || !this.getEditor()) {
42898         //    this.value = value;
42899             //this.setValue.defer(100,this,[value]);    
42900         //    return;
42901         //} 
42902         
42903         if(!this.getEditor()) {
42904             return;
42905         }
42906         
42907         this.getEditor().SetData(value);
42908         
42909         //
42910
42911     },
42912
42913     /**
42914      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42915      * @return {Mixed} value The field value
42916      */
42917     getValue : function()
42918     {
42919         
42920         if (this.frame && this.frame.dom.style.display == 'none') {
42921             return Roo.form.FCKeditor.superclass.getValue.call(this);
42922         }
42923         
42924         if(!this.el || !this.getEditor()) {
42925            
42926            // this.getValue.defer(100,this); 
42927             return this.value;
42928         }
42929        
42930         
42931         var value=this.getEditor().GetData();
42932         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42933         return Roo.form.FCKeditor.superclass.getValue.call(this);
42934         
42935
42936     },
42937
42938     /**
42939      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42940      * @return {Mixed} value The field value
42941      */
42942     getRawValue : function()
42943     {
42944         if (this.frame && this.frame.dom.style.display == 'none') {
42945             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42946         }
42947         
42948         if(!this.el || !this.getEditor()) {
42949             //this.getRawValue.defer(100,this); 
42950             return this.value;
42951             return;
42952         }
42953         
42954         
42955         
42956         var value=this.getEditor().GetData();
42957         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42958         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42959          
42960     },
42961     
42962     setSize : function(w,h) {
42963         
42964         
42965         
42966         //if (this.frame && this.frame.dom.style.display == 'none') {
42967         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42968         //    return;
42969         //}
42970         //if(!this.el || !this.getEditor()) {
42971         //    this.setSize.defer(100,this, [w,h]); 
42972         //    return;
42973         //}
42974         
42975         
42976         
42977         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42978         
42979         this.frame.dom.setAttribute('width', w);
42980         this.frame.dom.setAttribute('height', h);
42981         this.frame.setSize(w,h);
42982         
42983     },
42984     
42985     toggleSourceEdit : function(value) {
42986         
42987       
42988          
42989         this.el.dom.style.display = value ? '' : 'none';
42990         this.frame.dom.style.display = value ?  'none' : '';
42991         
42992     },
42993     
42994     
42995     focus: function(tag)
42996     {
42997         if (this.frame.dom.style.display == 'none') {
42998             return Roo.form.FCKeditor.superclass.focus.call(this);
42999         }
43000         if(!this.el || !this.getEditor()) {
43001             this.focus.defer(100,this, [tag]); 
43002             return;
43003         }
43004         
43005         
43006         
43007         
43008         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
43009         this.getEditor().Focus();
43010         if (tgs.length) {
43011             if (!this.getEditor().Selection.GetSelection()) {
43012                 this.focus.defer(100,this, [tag]); 
43013                 return;
43014             }
43015             
43016             
43017             var r = this.getEditor().EditorDocument.createRange();
43018             r.setStart(tgs[0],0);
43019             r.setEnd(tgs[0],0);
43020             this.getEditor().Selection.GetSelection().removeAllRanges();
43021             this.getEditor().Selection.GetSelection().addRange(r);
43022             this.getEditor().Focus();
43023         }
43024         
43025     },
43026     
43027     
43028     
43029     replaceTextarea : function()
43030     {
43031         if ( document.getElementById( this.getId() + '___Frame' ) )
43032             return ;
43033         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
43034         //{
43035             // We must check the elements firstly using the Id and then the name.
43036         var oTextarea = document.getElementById( this.getId() );
43037         
43038         var colElementsByName = document.getElementsByName( this.getId() ) ;
43039          
43040         oTextarea.style.display = 'none' ;
43041
43042         if ( oTextarea.tabIndex ) {            
43043             this.TabIndex = oTextarea.tabIndex ;
43044         }
43045         
43046         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
43047         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
43048         this.frame = Roo.get(this.getId() + '___Frame')
43049     },
43050     
43051     _getConfigHtml : function()
43052     {
43053         var sConfig = '' ;
43054
43055         for ( var o in this.fckconfig ) {
43056             sConfig += sConfig.length > 0  ? '&amp;' : '';
43057             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43058         }
43059
43060         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43061     },
43062     
43063     
43064     _getIFrameHtml : function()
43065     {
43066         var sFile = 'fckeditor.html' ;
43067         /* no idea what this is about..
43068         try
43069         {
43070             if ( (/fcksource=true/i).test( window.top.location.search ) )
43071                 sFile = 'fckeditor.original.html' ;
43072         }
43073         catch (e) { 
43074         */
43075
43076         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43077         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43078         
43079         
43080         var html = '<iframe id="' + this.getId() +
43081             '___Frame" src="' + sLink +
43082             '" width="' + this.width +
43083             '" height="' + this.height + '"' +
43084             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43085             ' frameborder="0" scrolling="no"></iframe>' ;
43086
43087         return html ;
43088     },
43089     
43090     _insertHtmlBefore : function( html, element )
43091     {
43092         if ( element.insertAdjacentHTML )       {
43093             // IE
43094             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43095         } else { // Gecko
43096             var oRange = document.createRange() ;
43097             oRange.setStartBefore( element ) ;
43098             var oFragment = oRange.createContextualFragment( html );
43099             element.parentNode.insertBefore( oFragment, element ) ;
43100         }
43101     }
43102     
43103     
43104   
43105     
43106     
43107     
43108     
43109
43110 });
43111
43112 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43113
43114 function FCKeditor_OnComplete(editorInstance){
43115     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43116     f.fckEditor = editorInstance;
43117     //console.log("loaded");
43118     f.fireEvent('editorinit', f, editorInstance);
43119
43120   
43121
43122  
43123
43124
43125
43126
43127
43128
43129
43130
43131
43132
43133
43134
43135
43136
43137
43138 //<script type="text/javascript">
43139 /**
43140  * @class Roo.form.GridField
43141  * @extends Roo.form.Field
43142  * Embed a grid (or editable grid into a form)
43143  * STATUS ALPHA
43144  * 
43145  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43146  * it needs 
43147  * xgrid.store = Roo.data.Store
43148  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43149  * xgrid.store.reader = Roo.data.JsonReader 
43150  * 
43151  * 
43152  * @constructor
43153  * Creates a new GridField
43154  * @param {Object} config Configuration options
43155  */
43156 Roo.form.GridField = function(config){
43157     Roo.form.GridField.superclass.constructor.call(this, config);
43158      
43159 };
43160
43161 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43162     /**
43163      * @cfg {Number} width  - used to restrict width of grid..
43164      */
43165     width : 100,
43166     /**
43167      * @cfg {Number} height - used to restrict height of grid..
43168      */
43169     height : 50,
43170      /**
43171      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43172          * 
43173          *}
43174      */
43175     xgrid : false, 
43176     /**
43177      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43178      * {tag: "input", type: "checkbox", autocomplete: "off"})
43179      */
43180    // defaultAutoCreate : { tag: 'div' },
43181     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43182     /**
43183      * @cfg {String} addTitle Text to include for adding a title.
43184      */
43185     addTitle : false,
43186     //
43187     onResize : function(){
43188         Roo.form.Field.superclass.onResize.apply(this, arguments);
43189     },
43190
43191     initEvents : function(){
43192         // Roo.form.Checkbox.superclass.initEvents.call(this);
43193         // has no events...
43194        
43195     },
43196
43197
43198     getResizeEl : function(){
43199         return this.wrap;
43200     },
43201
43202     getPositionEl : function(){
43203         return this.wrap;
43204     },
43205
43206     // private
43207     onRender : function(ct, position){
43208         
43209         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43210         var style = this.style;
43211         delete this.style;
43212         
43213         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43214         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43215         this.viewEl = this.wrap.createChild({ tag: 'div' });
43216         if (style) {
43217             this.viewEl.applyStyles(style);
43218         }
43219         if (this.width) {
43220             this.viewEl.setWidth(this.width);
43221         }
43222         if (this.height) {
43223             this.viewEl.setHeight(this.height);
43224         }
43225         //if(this.inputValue !== undefined){
43226         //this.setValue(this.value);
43227         
43228         
43229         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43230         
43231         
43232         this.grid.render();
43233         this.grid.getDataSource().on('remove', this.refreshValue, this);
43234         this.grid.getDataSource().on('update', this.refreshValue, this);
43235         this.grid.on('afteredit', this.refreshValue, this);
43236  
43237     },
43238      
43239     
43240     /**
43241      * Sets the value of the item. 
43242      * @param {String} either an object  or a string..
43243      */
43244     setValue : function(v){
43245         //this.value = v;
43246         v = v || []; // empty set..
43247         // this does not seem smart - it really only affects memoryproxy grids..
43248         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43249             var ds = this.grid.getDataSource();
43250             // assumes a json reader..
43251             var data = {}
43252             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43253             ds.loadData( data);
43254         }
43255         // clear selection so it does not get stale.
43256         if (this.grid.sm) { 
43257             this.grid.sm.clearSelections();
43258         }
43259         
43260         Roo.form.GridField.superclass.setValue.call(this, v);
43261         this.refreshValue();
43262         // should load data in the grid really....
43263     },
43264     
43265     // private
43266     refreshValue: function() {
43267          var val = [];
43268         this.grid.getDataSource().each(function(r) {
43269             val.push(r.data);
43270         });
43271         this.el.dom.value = Roo.encode(val);
43272     }
43273     
43274      
43275     
43276     
43277 });/*
43278  * Based on:
43279  * Ext JS Library 1.1.1
43280  * Copyright(c) 2006-2007, Ext JS, LLC.
43281  *
43282  * Originally Released Under LGPL - original licence link has changed is not relivant.
43283  *
43284  * Fork - LGPL
43285  * <script type="text/javascript">
43286  */
43287 /**
43288  * @class Roo.form.DisplayField
43289  * @extends Roo.form.Field
43290  * A generic Field to display non-editable data.
43291  * @constructor
43292  * Creates a new Display Field item.
43293  * @param {Object} config Configuration options
43294  */
43295 Roo.form.DisplayField = function(config){
43296     Roo.form.DisplayField.superclass.constructor.call(this, config);
43297     
43298 };
43299
43300 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43301     inputType:      'hidden',
43302     allowBlank:     true,
43303     readOnly:         true,
43304     
43305  
43306     /**
43307      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43308      */
43309     focusClass : undefined,
43310     /**
43311      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43312      */
43313     fieldClass: 'x-form-field',
43314     
43315      /**
43316      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43317      */
43318     valueRenderer: undefined,
43319     
43320     width: 100,
43321     /**
43322      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43323      * {tag: "input", type: "checkbox", autocomplete: "off"})
43324      */
43325      
43326  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43327
43328     onResize : function(){
43329         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43330         
43331     },
43332
43333     initEvents : function(){
43334         // Roo.form.Checkbox.superclass.initEvents.call(this);
43335         // has no events...
43336        
43337     },
43338
43339
43340     getResizeEl : function(){
43341         return this.wrap;
43342     },
43343
43344     getPositionEl : function(){
43345         return this.wrap;
43346     },
43347
43348     // private
43349     onRender : function(ct, position){
43350         
43351         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43352         //if(this.inputValue !== undefined){
43353         this.wrap = this.el.wrap();
43354         
43355         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43356         
43357         if (this.bodyStyle) {
43358             this.viewEl.applyStyles(this.bodyStyle);
43359         }
43360         //this.viewEl.setStyle('padding', '2px');
43361         
43362         this.setValue(this.value);
43363         
43364     },
43365 /*
43366     // private
43367     initValue : Roo.emptyFn,
43368
43369   */
43370
43371         // private
43372     onClick : function(){
43373         
43374     },
43375
43376     /**
43377      * Sets the checked state of the checkbox.
43378      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43379      */
43380     setValue : function(v){
43381         this.value = v;
43382         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43383         // this might be called before we have a dom element..
43384         if (!this.viewEl) {
43385             return;
43386         }
43387         this.viewEl.dom.innerHTML = html;
43388         Roo.form.DisplayField.superclass.setValue.call(this, v);
43389
43390     }
43391 });/*
43392  * 
43393  * Licence- LGPL
43394  * 
43395  */
43396
43397 /**
43398  * @class Roo.form.DayPicker
43399  * @extends Roo.form.Field
43400  * A Day picker show [M] [T] [W] ....
43401  * @constructor
43402  * Creates a new Day Picker
43403  * @param {Object} config Configuration options
43404  */
43405 Roo.form.DayPicker= function(config){
43406     Roo.form.DayPicker.superclass.constructor.call(this, config);
43407      
43408 };
43409
43410 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43411     /**
43412      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43413      */
43414     focusClass : undefined,
43415     /**
43416      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43417      */
43418     fieldClass: "x-form-field",
43419    
43420     /**
43421      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43422      * {tag: "input", type: "checkbox", autocomplete: "off"})
43423      */
43424     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43425     
43426    
43427     actionMode : 'viewEl', 
43428     //
43429     // private
43430  
43431     inputType : 'hidden',
43432     
43433      
43434     inputElement: false, // real input element?
43435     basedOn: false, // ????
43436     
43437     isFormField: true, // not sure where this is needed!!!!
43438
43439     onResize : function(){
43440         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43441         if(!this.boxLabel){
43442             this.el.alignTo(this.wrap, 'c-c');
43443         }
43444     },
43445
43446     initEvents : function(){
43447         Roo.form.Checkbox.superclass.initEvents.call(this);
43448         this.el.on("click", this.onClick,  this);
43449         this.el.on("change", this.onClick,  this);
43450     },
43451
43452
43453     getResizeEl : function(){
43454         return this.wrap;
43455     },
43456
43457     getPositionEl : function(){
43458         return this.wrap;
43459     },
43460
43461     
43462     // private
43463     onRender : function(ct, position){
43464         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43465        
43466         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43467         
43468         var r1 = '<table><tr>';
43469         var r2 = '<tr class="x-form-daypick-icons">';
43470         for (var i=0; i < 7; i++) {
43471             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43472             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43473         }
43474         
43475         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43476         viewEl.select('img').on('click', this.onClick, this);
43477         this.viewEl = viewEl;   
43478         
43479         
43480         // this will not work on Chrome!!!
43481         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43482         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43483         
43484         
43485           
43486
43487     },
43488
43489     // private
43490     initValue : Roo.emptyFn,
43491
43492     /**
43493      * Returns the checked state of the checkbox.
43494      * @return {Boolean} True if checked, else false
43495      */
43496     getValue : function(){
43497         return this.el.dom.value;
43498         
43499     },
43500
43501         // private
43502     onClick : function(e){ 
43503         //this.setChecked(!this.checked);
43504         Roo.get(e.target).toggleClass('x-menu-item-checked');
43505         this.refreshValue();
43506         //if(this.el.dom.checked != this.checked){
43507         //    this.setValue(this.el.dom.checked);
43508        // }
43509     },
43510     
43511     // private
43512     refreshValue : function()
43513     {
43514         var val = '';
43515         this.viewEl.select('img',true).each(function(e,i,n)  {
43516             val += e.is(".x-menu-item-checked") ? String(n) : '';
43517         });
43518         this.setValue(val, true);
43519     },
43520
43521     /**
43522      * Sets the checked state of the checkbox.
43523      * On is always based on a string comparison between inputValue and the param.
43524      * @param {Boolean/String} value - the value to set 
43525      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43526      */
43527     setValue : function(v,suppressEvent){
43528         if (!this.el.dom) {
43529             return;
43530         }
43531         var old = this.el.dom.value ;
43532         this.el.dom.value = v;
43533         if (suppressEvent) {
43534             return ;
43535         }
43536          
43537         // update display..
43538         this.viewEl.select('img',true).each(function(e,i,n)  {
43539             
43540             var on = e.is(".x-menu-item-checked");
43541             var newv = v.indexOf(String(n)) > -1;
43542             if (on != newv) {
43543                 e.toggleClass('x-menu-item-checked');
43544             }
43545             
43546         });
43547         
43548         
43549         this.fireEvent('change', this, v, old);
43550         
43551         
43552     },
43553    
43554     // handle setting of hidden value by some other method!!?!?
43555     setFromHidden: function()
43556     {
43557         if(!this.el){
43558             return;
43559         }
43560         //console.log("SET FROM HIDDEN");
43561         //alert('setFrom hidden');
43562         this.setValue(this.el.dom.value);
43563     },
43564     
43565     onDestroy : function()
43566     {
43567         if(this.viewEl){
43568             Roo.get(this.viewEl).remove();
43569         }
43570          
43571         Roo.form.DayPicker.superclass.onDestroy.call(this);
43572     }
43573
43574 });/*
43575  * RooJS Library 1.1.1
43576  * Copyright(c) 2008-2011  Alan Knowles
43577  *
43578  * License - LGPL
43579  */
43580  
43581
43582 /**
43583  * @class Roo.form.ComboCheck
43584  * @extends Roo.form.ComboBox
43585  * A combobox for multiple select items.
43586  *
43587  * FIXME - could do with a reset button..
43588  * 
43589  * @constructor
43590  * Create a new ComboCheck
43591  * @param {Object} config Configuration options
43592  */
43593 Roo.form.ComboCheck = function(config){
43594     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43595     // should verify some data...
43596     // like
43597     // hiddenName = required..
43598     // displayField = required
43599     // valudField == required
43600     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43601     var _t = this;
43602     Roo.each(req, function(e) {
43603         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43604             throw "Roo.form.ComboCheck : missing value for: " + e;
43605         }
43606     });
43607     
43608     
43609 };
43610
43611 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
43612      
43613      
43614     editable : false,
43615      
43616     selectedClass: 'x-menu-item-checked', 
43617     
43618     // private
43619     onRender : function(ct, position){
43620         var _t = this;
43621         
43622         
43623         
43624         if(!this.tpl){
43625             var cls = 'x-combo-list';
43626
43627             
43628             this.tpl =  new Roo.Template({
43629                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
43630                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
43631                    '<span>{' + this.displayField + '}</span>' +
43632                     '</div>' 
43633                 
43634             });
43635         }
43636  
43637         
43638         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
43639         this.view.singleSelect = false;
43640         this.view.multiSelect = true;
43641         this.view.toggleSelect = true;
43642         this.pageTb.add(new Roo.Toolbar.Fill(), {
43643             
43644             text: 'Done',
43645             handler: function()
43646             {
43647                 _t.collapse();
43648             }
43649         });
43650     },
43651     
43652     onViewOver : function(e, t){
43653         // do nothing...
43654         return;
43655         
43656     },
43657     
43658     onViewClick : function(doFocus,index){
43659         return;
43660         
43661     },
43662     select: function () {
43663         //Roo.log("SELECT CALLED");
43664     },
43665      
43666     selectByValue : function(xv, scrollIntoView){
43667         var ar = this.getValueArray();
43668         var sels = [];
43669         
43670         Roo.each(ar, function(v) {
43671             if(v === undefined || v === null){
43672                 return;
43673             }
43674             var r = this.findRecord(this.valueField, v);
43675             if(r){
43676                 sels.push(this.store.indexOf(r))
43677                 
43678             }
43679         },this);
43680         this.view.select(sels);
43681         return false;
43682     },
43683     
43684     
43685     
43686     onSelect : function(record, index){
43687        // Roo.log("onselect Called");
43688        // this is only called by the clear button now..
43689         this.view.clearSelections();
43690         this.setValue('[]');
43691         if (this.value != this.valueBefore) {
43692             this.fireEvent('change', this, this.value, this.valueBefore);
43693         }
43694     },
43695     getValueArray : function()
43696     {
43697         var ar = [] ;
43698         
43699         try {
43700             Roo.log(this.value);
43701             var ar = Roo.decode(this.value);
43702             return  ar instanceof Array ? ar : []; //?? valid?
43703             
43704         } catch(e) {
43705             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
43706             return [];
43707         }
43708          
43709     },
43710     expand : function ()
43711     {
43712         Roo.form.ComboCheck.superclass.expand.call(this);
43713         this.valueBefore = this.value;
43714         
43715
43716     },
43717     
43718     collapse : function(){
43719         Roo.form.ComboCheck.superclass.collapse.call(this);
43720         var sl = this.view.getSelectedIndexes();
43721         var st = this.store;
43722         var nv = [];
43723         var tv = [];
43724         var r;
43725         Roo.each(sl, function(i) {
43726             r = st.getAt(i);
43727             nv.push(r.get(this.valueField));
43728         },this);
43729         this.setValue(Roo.encode(nv));
43730         if (this.value != this.valueBefore) {
43731
43732             this.fireEvent('change', this, this.value, this.valueBefore);
43733         }
43734         
43735     },
43736     
43737     setValue : function(v){
43738         // Roo.log(v);
43739         this.value = v;
43740         
43741         var vals = this.getValueArray();
43742         var tv = [];
43743         Roo.each(vals, function(k) {
43744             var r = this.findRecord(this.valueField, k);
43745             if(r){
43746                 tv.push(r.data[this.displayField]);
43747             }else if(this.valueNotFoundText !== undefined){
43748                 tv.push( this.valueNotFoundText );
43749             }
43750         },this);
43751        // Roo.log(tv);
43752         
43753         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
43754         this.hiddenField.value = v;
43755         this.value = v;
43756     }
43757     
43758 });//<script type="text/javasscript">
43759  
43760
43761 /**
43762  * @class Roo.DDView
43763  * A DnD enabled version of Roo.View.
43764  * @param {Element/String} container The Element in which to create the View.
43765  * @param {String} tpl The template string used to create the markup for each element of the View
43766  * @param {Object} config The configuration properties. These include all the config options of
43767  * {@link Roo.View} plus some specific to this class.<br>
43768  * <p>
43769  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
43770  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
43771  * <p>
43772  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
43773 .x-view-drag-insert-above {
43774         border-top:1px dotted #3366cc;
43775 }
43776 .x-view-drag-insert-below {
43777         border-bottom:1px dotted #3366cc;
43778 }
43779 </code></pre>
43780  * 
43781  */
43782  
43783 Roo.DDView = function(container, tpl, config) {
43784     Roo.DDView.superclass.constructor.apply(this, arguments);
43785     this.getEl().setStyle("outline", "0px none");
43786     this.getEl().unselectable();
43787     if (this.dragGroup) {
43788                 this.setDraggable(this.dragGroup.split(","));
43789     }
43790     if (this.dropGroup) {
43791                 this.setDroppable(this.dropGroup.split(","));
43792     }
43793     if (this.deletable) {
43794         this.setDeletable();
43795     }
43796     this.isDirtyFlag = false;
43797         this.addEvents({
43798                 "drop" : true
43799         });
43800 };
43801
43802 Roo.extend(Roo.DDView, Roo.View, {
43803 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
43804 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
43805 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
43806 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
43807
43808         isFormField: true,
43809
43810         reset: Roo.emptyFn,
43811         
43812         clearInvalid: Roo.form.Field.prototype.clearInvalid,
43813
43814         validate: function() {
43815                 return true;
43816         },
43817         
43818         destroy: function() {
43819                 this.purgeListeners();
43820                 this.getEl.removeAllListeners();
43821                 this.getEl().remove();
43822                 if (this.dragZone) {
43823                         if (this.dragZone.destroy) {
43824                                 this.dragZone.destroy();
43825                         }
43826                 }
43827                 if (this.dropZone) {
43828                         if (this.dropZone.destroy) {
43829                                 this.dropZone.destroy();
43830                         }
43831                 }
43832         },
43833
43834 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
43835         getName: function() {
43836                 return this.name;
43837         },
43838
43839 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43840         setValue: function(v) {
43841                 if (!this.store) {
43842                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43843                 }
43844                 var data = {};
43845                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43846                 this.store.proxy = new Roo.data.MemoryProxy(data);
43847                 this.store.load();
43848         },
43849
43850 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43851         getValue: function() {
43852                 var result = '(';
43853                 this.store.each(function(rec) {
43854                         result += rec.id + ',';
43855                 });
43856                 return result.substr(0, result.length - 1) + ')';
43857         },
43858         
43859         getIds: function() {
43860                 var i = 0, result = new Array(this.store.getCount());
43861                 this.store.each(function(rec) {
43862                         result[i++] = rec.id;
43863                 });
43864                 return result;
43865         },
43866         
43867         isDirty: function() {
43868                 return this.isDirtyFlag;
43869         },
43870
43871 /**
43872  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43873  *      whole Element becomes the target, and this causes the drop gesture to append.
43874  */
43875     getTargetFromEvent : function(e) {
43876                 var target = e.getTarget();
43877                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43878                 target = target.parentNode;
43879                 }
43880                 if (!target) {
43881                         target = this.el.dom.lastChild || this.el.dom;
43882                 }
43883                 return target;
43884     },
43885
43886 /**
43887  *      Create the drag data which consists of an object which has the property "ddel" as
43888  *      the drag proxy element. 
43889  */
43890     getDragData : function(e) {
43891         var target = this.findItemFromChild(e.getTarget());
43892                 if(target) {
43893                         this.handleSelection(e);
43894                         var selNodes = this.getSelectedNodes();
43895             var dragData = {
43896                 source: this,
43897                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43898                 nodes: selNodes,
43899                 records: []
43900                         };
43901                         var selectedIndices = this.getSelectedIndexes();
43902                         for (var i = 0; i < selectedIndices.length; i++) {
43903                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43904                         }
43905                         if (selNodes.length == 1) {
43906                                 dragData.ddel = target.cloneNode(true); // the div element
43907                         } else {
43908                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43909                                 div.className = 'multi-proxy';
43910                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43911                                         div.appendChild(selNodes[i].cloneNode(true));
43912                                 }
43913                                 dragData.ddel = div;
43914                         }
43915             //console.log(dragData)
43916             //console.log(dragData.ddel.innerHTML)
43917                         return dragData;
43918                 }
43919         //console.log('nodragData')
43920                 return false;
43921     },
43922     
43923 /**     Specify to which ddGroup items in this DDView may be dragged. */
43924     setDraggable: function(ddGroup) {
43925         if (ddGroup instanceof Array) {
43926                 Roo.each(ddGroup, this.setDraggable, this);
43927                 return;
43928         }
43929         if (this.dragZone) {
43930                 this.dragZone.addToGroup(ddGroup);
43931         } else {
43932                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
43933                                 containerScroll: true,
43934                                 ddGroup: ddGroup 
43935
43936                         });
43937 //                      Draggability implies selection. DragZone's mousedown selects the element.
43938                         if (!this.multiSelect) { this.singleSelect = true; }
43939
43940 //                      Wire the DragZone's handlers up to methods in *this*
43941                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
43942                 }
43943     },
43944
43945 /**     Specify from which ddGroup this DDView accepts drops. */
43946     setDroppable: function(ddGroup) {
43947         if (ddGroup instanceof Array) {
43948                 Roo.each(ddGroup, this.setDroppable, this);
43949                 return;
43950         }
43951         if (this.dropZone) {
43952                 this.dropZone.addToGroup(ddGroup);
43953         } else {
43954                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
43955                                 containerScroll: true,
43956                                 ddGroup: ddGroup
43957                         });
43958
43959 //                      Wire the DropZone's handlers up to methods in *this*
43960                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
43961                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
43962                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
43963                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
43964                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
43965                 }
43966     },
43967
43968 /**     Decide whether to drop above or below a View node. */
43969     getDropPoint : function(e, n, dd){
43970         if (n == this.el.dom) { return "above"; }
43971                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
43972                 var c = t + (b - t) / 2;
43973                 var y = Roo.lib.Event.getPageY(e);
43974                 if(y <= c) {
43975                         return "above";
43976                 }else{
43977                         return "below";
43978                 }
43979     },
43980
43981     onNodeEnter : function(n, dd, e, data){
43982                 return false;
43983     },
43984     
43985     onNodeOver : function(n, dd, e, data){
43986                 var pt = this.getDropPoint(e, n, dd);
43987                 // set the insert point style on the target node
43988                 var dragElClass = this.dropNotAllowed;
43989                 if (pt) {
43990                         var targetElClass;
43991                         if (pt == "above"){
43992                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
43993                                 targetElClass = "x-view-drag-insert-above";
43994                         } else {
43995                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
43996                                 targetElClass = "x-view-drag-insert-below";
43997                         }
43998                         if (this.lastInsertClass != targetElClass){
43999                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
44000                                 this.lastInsertClass = targetElClass;
44001                         }
44002                 }
44003                 return dragElClass;
44004         },
44005
44006     onNodeOut : function(n, dd, e, data){
44007                 this.removeDropIndicators(n);
44008     },
44009
44010     onNodeDrop : function(n, dd, e, data){
44011         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
44012                 return false;
44013         }
44014         var pt = this.getDropPoint(e, n, dd);
44015                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
44016                 if (pt == "below") { insertAt++; }
44017                 for (var i = 0; i < data.records.length; i++) {
44018                         var r = data.records[i];
44019                         var dup = this.store.getById(r.id);
44020                         if (dup && (dd != this.dragZone)) {
44021                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
44022                         } else {
44023                                 if (data.copy) {
44024                                         this.store.insert(insertAt++, r.copy());
44025                                 } else {
44026                                         data.source.isDirtyFlag = true;
44027                                         r.store.remove(r);
44028                                         this.store.insert(insertAt++, r);
44029                                 }
44030                                 this.isDirtyFlag = true;
44031                         }
44032                 }
44033                 this.dragZone.cachedTarget = null;
44034                 return true;
44035     },
44036
44037     removeDropIndicators : function(n){
44038                 if(n){
44039                         Roo.fly(n).removeClass([
44040                                 "x-view-drag-insert-above",
44041                                 "x-view-drag-insert-below"]);
44042                         this.lastInsertClass = "_noclass";
44043                 }
44044     },
44045
44046 /**
44047  *      Utility method. Add a delete option to the DDView's context menu.
44048  *      @param {String} imageUrl The URL of the "delete" icon image.
44049  */
44050         setDeletable: function(imageUrl) {
44051                 if (!this.singleSelect && !this.multiSelect) {
44052                         this.singleSelect = true;
44053                 }
44054                 var c = this.getContextMenu();
44055                 this.contextMenu.on("itemclick", function(item) {
44056                         switch (item.id) {
44057                                 case "delete":
44058                                         this.remove(this.getSelectedIndexes());
44059                                         break;
44060                         }
44061                 }, this);
44062                 this.contextMenu.add({
44063                         icon: imageUrl,
44064                         id: "delete",
44065                         text: 'Delete'
44066                 });
44067         },
44068         
44069 /**     Return the context menu for this DDView. */
44070         getContextMenu: function() {
44071                 if (!this.contextMenu) {
44072 //                      Create the View's context menu
44073                         this.contextMenu = new Roo.menu.Menu({
44074                                 id: this.id + "-contextmenu"
44075                         });
44076                         this.el.on("contextmenu", this.showContextMenu, this);
44077                 }
44078                 return this.contextMenu;
44079         },
44080         
44081         disableContextMenu: function() {
44082                 if (this.contextMenu) {
44083                         this.el.un("contextmenu", this.showContextMenu, this);
44084                 }
44085         },
44086
44087         showContextMenu: function(e, item) {
44088         item = this.findItemFromChild(e.getTarget());
44089                 if (item) {
44090                         e.stopEvent();
44091                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44092                         this.contextMenu.showAt(e.getXY());
44093             }
44094     },
44095
44096 /**
44097  *      Remove {@link Roo.data.Record}s at the specified indices.
44098  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44099  */
44100     remove: function(selectedIndices) {
44101                 selectedIndices = [].concat(selectedIndices);
44102                 for (var i = 0; i < selectedIndices.length; i++) {
44103                         var rec = this.store.getAt(selectedIndices[i]);
44104                         this.store.remove(rec);
44105                 }
44106     },
44107
44108 /**
44109  *      Double click fires the event, but also, if this is draggable, and there is only one other
44110  *      related DropZone, it transfers the selected node.
44111  */
44112     onDblClick : function(e){
44113         var item = this.findItemFromChild(e.getTarget());
44114         if(item){
44115             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44116                 return false;
44117             }
44118             if (this.dragGroup) {
44119                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44120                     while (targets.indexOf(this.dropZone) > -1) {
44121                             targets.remove(this.dropZone);
44122                                 }
44123                     if (targets.length == 1) {
44124                                         this.dragZone.cachedTarget = null;
44125                         var el = Roo.get(targets[0].getEl());
44126                         var box = el.getBox(true);
44127                         targets[0].onNodeDrop(el.dom, {
44128                                 target: el.dom,
44129                                 xy: [box.x, box.y + box.height - 1]
44130                         }, null, this.getDragData(e));
44131                     }
44132                 }
44133         }
44134     },
44135     
44136     handleSelection: function(e) {
44137                 this.dragZone.cachedTarget = null;
44138         var item = this.findItemFromChild(e.getTarget());
44139         if (!item) {
44140                 this.clearSelections(true);
44141                 return;
44142         }
44143                 if (item && (this.multiSelect || this.singleSelect)){
44144                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44145                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44146                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44147                                 this.unselect(item);
44148                         } else {
44149                                 this.select(item, this.multiSelect && e.ctrlKey);
44150                                 this.lastSelection = item;
44151                         }
44152                 }
44153     },
44154
44155     onItemClick : function(item, index, e){
44156                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44157                         return false;
44158                 }
44159                 return true;
44160     },
44161
44162     unselect : function(nodeInfo, suppressEvent){
44163                 var node = this.getNode(nodeInfo);
44164                 if(node && this.isSelected(node)){
44165                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44166                                 Roo.fly(node).removeClass(this.selectedClass);
44167                                 this.selections.remove(node);
44168                                 if(!suppressEvent){
44169                                         this.fireEvent("selectionchange", this, this.selections);
44170                                 }
44171                         }
44172                 }
44173     }
44174 });
44175 /*
44176  * Based on:
44177  * Ext JS Library 1.1.1
44178  * Copyright(c) 2006-2007, Ext JS, LLC.
44179  *
44180  * Originally Released Under LGPL - original licence link has changed is not relivant.
44181  *
44182  * Fork - LGPL
44183  * <script type="text/javascript">
44184  */
44185  
44186 /**
44187  * @class Roo.LayoutManager
44188  * @extends Roo.util.Observable
44189  * Base class for layout managers.
44190  */
44191 Roo.LayoutManager = function(container, config){
44192     Roo.LayoutManager.superclass.constructor.call(this);
44193     this.el = Roo.get(container);
44194     // ie scrollbar fix
44195     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44196         document.body.scroll = "no";
44197     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44198         this.el.position('relative');
44199     }
44200     this.id = this.el.id;
44201     this.el.addClass("x-layout-container");
44202     /** false to disable window resize monitoring @type Boolean */
44203     this.monitorWindowResize = true;
44204     this.regions = {};
44205     this.addEvents({
44206         /**
44207          * @event layout
44208          * Fires when a layout is performed. 
44209          * @param {Roo.LayoutManager} this
44210          */
44211         "layout" : true,
44212         /**
44213          * @event regionresized
44214          * Fires when the user resizes a region. 
44215          * @param {Roo.LayoutRegion} region The resized region
44216          * @param {Number} newSize The new size (width for east/west, height for north/south)
44217          */
44218         "regionresized" : true,
44219         /**
44220          * @event regioncollapsed
44221          * Fires when a region is collapsed. 
44222          * @param {Roo.LayoutRegion} region The collapsed region
44223          */
44224         "regioncollapsed" : true,
44225         /**
44226          * @event regionexpanded
44227          * Fires when a region is expanded.  
44228          * @param {Roo.LayoutRegion} region The expanded region
44229          */
44230         "regionexpanded" : true
44231     });
44232     this.updating = false;
44233     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44234 };
44235
44236 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44237     /**
44238      * Returns true if this layout is currently being updated
44239      * @return {Boolean}
44240      */
44241     isUpdating : function(){
44242         return this.updating; 
44243     },
44244     
44245     /**
44246      * Suspend the LayoutManager from doing auto-layouts while
44247      * making multiple add or remove calls
44248      */
44249     beginUpdate : function(){
44250         this.updating = true;    
44251     },
44252     
44253     /**
44254      * Restore auto-layouts and optionally disable the manager from performing a layout
44255      * @param {Boolean} noLayout true to disable a layout update 
44256      */
44257     endUpdate : function(noLayout){
44258         this.updating = false;
44259         if(!noLayout){
44260             this.layout();
44261         }    
44262     },
44263     
44264     layout: function(){
44265         
44266     },
44267     
44268     onRegionResized : function(region, newSize){
44269         this.fireEvent("regionresized", region, newSize);
44270         this.layout();
44271     },
44272     
44273     onRegionCollapsed : function(region){
44274         this.fireEvent("regioncollapsed", region);
44275     },
44276     
44277     onRegionExpanded : function(region){
44278         this.fireEvent("regionexpanded", region);
44279     },
44280         
44281     /**
44282      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44283      * performs box-model adjustments.
44284      * @return {Object} The size as an object {width: (the width), height: (the height)}
44285      */
44286     getViewSize : function(){
44287         var size;
44288         if(this.el.dom != document.body){
44289             size = this.el.getSize();
44290         }else{
44291             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44292         }
44293         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44294         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44295         return size;
44296     },
44297     
44298     /**
44299      * Returns the Element this layout is bound to.
44300      * @return {Roo.Element}
44301      */
44302     getEl : function(){
44303         return this.el;
44304     },
44305     
44306     /**
44307      * Returns the specified region.
44308      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
44309      * @return {Roo.LayoutRegion}
44310      */
44311     getRegion : function(target){
44312         return this.regions[target.toLowerCase()];
44313     },
44314     
44315     onWindowResize : function(){
44316         if(this.monitorWindowResize){
44317             this.layout();
44318         }
44319     }
44320 });/*
44321  * Based on:
44322  * Ext JS Library 1.1.1
44323  * Copyright(c) 2006-2007, Ext JS, LLC.
44324  *
44325  * Originally Released Under LGPL - original licence link has changed is not relivant.
44326  *
44327  * Fork - LGPL
44328  * <script type="text/javascript">
44329  */
44330 /**
44331  * @class Roo.BorderLayout
44332  * @extends Roo.LayoutManager
44333  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
44334  * please see: <br><br>
44335  * <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>
44336  * <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>
44337  * Example:
44338  <pre><code>
44339  var layout = new Roo.BorderLayout(document.body, {
44340     north: {
44341         initialSize: 25,
44342         titlebar: false
44343     },
44344     west: {
44345         split:true,
44346         initialSize: 200,
44347         minSize: 175,
44348         maxSize: 400,
44349         titlebar: true,
44350         collapsible: true
44351     },
44352     east: {
44353         split:true,
44354         initialSize: 202,
44355         minSize: 175,
44356         maxSize: 400,
44357         titlebar: true,
44358         collapsible: true
44359     },
44360     south: {
44361         split:true,
44362         initialSize: 100,
44363         minSize: 100,
44364         maxSize: 200,
44365         titlebar: true,
44366         collapsible: true
44367     },
44368     center: {
44369         titlebar: true,
44370         autoScroll:true,
44371         resizeTabs: true,
44372         minTabWidth: 50,
44373         preferredTabWidth: 150
44374     }
44375 });
44376
44377 // shorthand
44378 var CP = Roo.ContentPanel;
44379
44380 layout.beginUpdate();
44381 layout.add("north", new CP("north", "North"));
44382 layout.add("south", new CP("south", {title: "South", closable: true}));
44383 layout.add("west", new CP("west", {title: "West"}));
44384 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44385 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44386 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44387 layout.getRegion("center").showPanel("center1");
44388 layout.endUpdate();
44389 </code></pre>
44390
44391 <b>The container the layout is rendered into can be either the body element or any other element.
44392 If it is not the body element, the container needs to either be an absolute positioned element,
44393 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44394 the container size if it is not the body element.</b>
44395
44396 * @constructor
44397 * Create a new BorderLayout
44398 * @param {String/HTMLElement/Element} container The container this layout is bound to
44399 * @param {Object} config Configuration options
44400  */
44401 Roo.BorderLayout = function(container, config){
44402     config = config || {};
44403     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44404     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44405     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44406         var target = this.factory.validRegions[i];
44407         if(config[target]){
44408             this.addRegion(target, config[target]);
44409         }
44410     }
44411 };
44412
44413 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44414     /**
44415      * Creates and adds a new region if it doesn't already exist.
44416      * @param {String} target The target region key (north, south, east, west or center).
44417      * @param {Object} config The regions config object
44418      * @return {BorderLayoutRegion} The new region
44419      */
44420     addRegion : function(target, config){
44421         if(!this.regions[target]){
44422             var r = this.factory.create(target, this, config);
44423             this.bindRegion(target, r);
44424         }
44425         return this.regions[target];
44426     },
44427
44428     // private (kinda)
44429     bindRegion : function(name, r){
44430         this.regions[name] = r;
44431         r.on("visibilitychange", this.layout, this);
44432         r.on("paneladded", this.layout, this);
44433         r.on("panelremoved", this.layout, this);
44434         r.on("invalidated", this.layout, this);
44435         r.on("resized", this.onRegionResized, this);
44436         r.on("collapsed", this.onRegionCollapsed, this);
44437         r.on("expanded", this.onRegionExpanded, this);
44438     },
44439
44440     /**
44441      * Performs a layout update.
44442      */
44443     layout : function(){
44444         if(this.updating) return;
44445         var size = this.getViewSize();
44446         var w = size.width;
44447         var h = size.height;
44448         var centerW = w;
44449         var centerH = h;
44450         var centerY = 0;
44451         var centerX = 0;
44452         //var x = 0, y = 0;
44453
44454         var rs = this.regions;
44455         var north = rs["north"];
44456         var south = rs["south"]; 
44457         var west = rs["west"];
44458         var east = rs["east"];
44459         var center = rs["center"];
44460         //if(this.hideOnLayout){ // not supported anymore
44461             //c.el.setStyle("display", "none");
44462         //}
44463         if(north && north.isVisible()){
44464             var b = north.getBox();
44465             var m = north.getMargins();
44466             b.width = w - (m.left+m.right);
44467             b.x = m.left;
44468             b.y = m.top;
44469             centerY = b.height + b.y + m.bottom;
44470             centerH -= centerY;
44471             north.updateBox(this.safeBox(b));
44472         }
44473         if(south && south.isVisible()){
44474             var b = south.getBox();
44475             var m = south.getMargins();
44476             b.width = w - (m.left+m.right);
44477             b.x = m.left;
44478             var totalHeight = (b.height + m.top + m.bottom);
44479             b.y = h - totalHeight + m.top;
44480             centerH -= totalHeight;
44481             south.updateBox(this.safeBox(b));
44482         }
44483         if(west && west.isVisible()){
44484             var b = west.getBox();
44485             var m = west.getMargins();
44486             b.height = centerH - (m.top+m.bottom);
44487             b.x = m.left;
44488             b.y = centerY + m.top;
44489             var totalWidth = (b.width + m.left + m.right);
44490             centerX += totalWidth;
44491             centerW -= totalWidth;
44492             west.updateBox(this.safeBox(b));
44493         }
44494         if(east && east.isVisible()){
44495             var b = east.getBox();
44496             var m = east.getMargins();
44497             b.height = centerH - (m.top+m.bottom);
44498             var totalWidth = (b.width + m.left + m.right);
44499             b.x = w - totalWidth + m.left;
44500             b.y = centerY + m.top;
44501             centerW -= totalWidth;
44502             east.updateBox(this.safeBox(b));
44503         }
44504         if(center){
44505             var m = center.getMargins();
44506             var centerBox = {
44507                 x: centerX + m.left,
44508                 y: centerY + m.top,
44509                 width: centerW - (m.left+m.right),
44510                 height: centerH - (m.top+m.bottom)
44511             };
44512             //if(this.hideOnLayout){
44513                 //center.el.setStyle("display", "block");
44514             //}
44515             center.updateBox(this.safeBox(centerBox));
44516         }
44517         this.el.repaint();
44518         this.fireEvent("layout", this);
44519     },
44520
44521     // private
44522     safeBox : function(box){
44523         box.width = Math.max(0, box.width);
44524         box.height = Math.max(0, box.height);
44525         return box;
44526     },
44527
44528     /**
44529      * Adds a ContentPanel (or subclass) to this layout.
44530      * @param {String} target The target region key (north, south, east, west or center).
44531      * @param {Roo.ContentPanel} panel The panel to add
44532      * @return {Roo.ContentPanel} The added panel
44533      */
44534     add : function(target, panel){
44535          
44536         target = target.toLowerCase();
44537         return this.regions[target].add(panel);
44538     },
44539
44540     /**
44541      * Remove a ContentPanel (or subclass) to this layout.
44542      * @param {String} target The target region key (north, south, east, west or center).
44543      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44544      * @return {Roo.ContentPanel} The removed panel
44545      */
44546     remove : function(target, panel){
44547         target = target.toLowerCase();
44548         return this.regions[target].remove(panel);
44549     },
44550
44551     /**
44552      * Searches all regions for a panel with the specified id
44553      * @param {String} panelId
44554      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44555      */
44556     findPanel : function(panelId){
44557         var rs = this.regions;
44558         for(var target in rs){
44559             if(typeof rs[target] != "function"){
44560                 var p = rs[target].getPanel(panelId);
44561                 if(p){
44562                     return p;
44563                 }
44564             }
44565         }
44566         return null;
44567     },
44568
44569     /**
44570      * Searches all regions for a panel with the specified id and activates (shows) it.
44571      * @param {String/ContentPanel} panelId The panels id or the panel itself
44572      * @return {Roo.ContentPanel} The shown panel or null
44573      */
44574     showPanel : function(panelId) {
44575       var rs = this.regions;
44576       for(var target in rs){
44577          var r = rs[target];
44578          if(typeof r != "function"){
44579             if(r.hasPanel(panelId)){
44580                return r.showPanel(panelId);
44581             }
44582          }
44583       }
44584       return null;
44585    },
44586
44587    /**
44588      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44589      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44590      */
44591     restoreState : function(provider){
44592         if(!provider){
44593             provider = Roo.state.Manager;
44594         }
44595         var sm = new Roo.LayoutStateManager();
44596         sm.init(this, provider);
44597     },
44598
44599     /**
44600      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44601      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44602      * a valid ContentPanel config object.  Example:
44603      * <pre><code>
44604 // Create the main layout
44605 var layout = new Roo.BorderLayout('main-ct', {
44606     west: {
44607         split:true,
44608         minSize: 175,
44609         titlebar: true
44610     },
44611     center: {
44612         title:'Components'
44613     }
44614 }, 'main-ct');
44615
44616 // Create and add multiple ContentPanels at once via configs
44617 layout.batchAdd({
44618    west: {
44619        id: 'source-files',
44620        autoCreate:true,
44621        title:'Ext Source Files',
44622        autoScroll:true,
44623        fitToFrame:true
44624    },
44625    center : {
44626        el: cview,
44627        autoScroll:true,
44628        fitToFrame:true,
44629        toolbar: tb,
44630        resizeEl:'cbody'
44631    }
44632 });
44633 </code></pre>
44634      * @param {Object} regions An object containing ContentPanel configs by region name
44635      */
44636     batchAdd : function(regions){
44637         this.beginUpdate();
44638         for(var rname in regions){
44639             var lr = this.regions[rname];
44640             if(lr){
44641                 this.addTypedPanels(lr, regions[rname]);
44642             }
44643         }
44644         this.endUpdate();
44645     },
44646
44647     // private
44648     addTypedPanels : function(lr, ps){
44649         if(typeof ps == 'string'){
44650             lr.add(new Roo.ContentPanel(ps));
44651         }
44652         else if(ps instanceof Array){
44653             for(var i =0, len = ps.length; i < len; i++){
44654                 this.addTypedPanels(lr, ps[i]);
44655             }
44656         }
44657         else if(!ps.events){ // raw config?
44658             var el = ps.el;
44659             delete ps.el; // prevent conflict
44660             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44661         }
44662         else {  // panel object assumed!
44663             lr.add(ps);
44664         }
44665     },
44666     /**
44667      * Adds a xtype elements to the layout.
44668      * <pre><code>
44669
44670 layout.addxtype({
44671        xtype : 'ContentPanel',
44672        region: 'west',
44673        items: [ .... ]
44674    }
44675 );
44676
44677 layout.addxtype({
44678         xtype : 'NestedLayoutPanel',
44679         region: 'west',
44680         layout: {
44681            center: { },
44682            west: { }   
44683         },
44684         items : [ ... list of content panels or nested layout panels.. ]
44685    }
44686 );
44687 </code></pre>
44688      * @param {Object} cfg Xtype definition of item to add.
44689      */
44690     addxtype : function(cfg)
44691     {
44692         // basically accepts a pannel...
44693         // can accept a layout region..!?!?
44694         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
44695         
44696         if (!cfg.xtype.match(/Panel$/)) {
44697             return false;
44698         }
44699         var ret = false;
44700         
44701         if (typeof(cfg.region) == 'undefined') {
44702             Roo.log("Failed to add Panel, region was not set");
44703             Roo.log(cfg);
44704             return false;
44705         }
44706         var region = cfg.region;
44707         delete cfg.region;
44708         
44709           
44710         var xitems = [];
44711         if (cfg.items) {
44712             xitems = cfg.items;
44713             delete cfg.items;
44714         }
44715         
44716         
44717         switch(cfg.xtype) 
44718         {
44719             case 'ContentPanel':  // ContentPanel (el, cfg)
44720             case 'ScrollPanel':  // ContentPanel (el, cfg)
44721                 if(cfg.autoCreate) {
44722                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44723                 } else {
44724                     var el = this.el.createChild();
44725                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44726                 }
44727                 
44728                 this.add(region, ret);
44729                 break;
44730             
44731             
44732             case 'TreePanel': // our new panel!
44733                 cfg.el = this.el.createChild();
44734                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44735                 this.add(region, ret);
44736                 break;
44737             
44738             case 'NestedLayoutPanel': 
44739                 // create a new Layout (which is  a Border Layout...
44740                 var el = this.el.createChild();
44741                 var clayout = cfg.layout;
44742                 delete cfg.layout;
44743                 clayout.items   = clayout.items  || [];
44744                 // replace this exitems with the clayout ones..
44745                 xitems = clayout.items;
44746                  
44747                 
44748                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
44749                     cfg.background = false;
44750                 }
44751                 var layout = new Roo.BorderLayout(el, clayout);
44752                 
44753                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
44754                 //console.log('adding nested layout panel '  + cfg.toSource());
44755                 this.add(region, ret);
44756                 
44757                 break;
44758                 
44759             case 'GridPanel': 
44760             
44761                 // needs grid and region
44762                 
44763                 //var el = this.getRegion(region).el.createChild();
44764                 var el = this.el.createChild();
44765                 // create the grid first...
44766                 
44767                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
44768                 delete cfg.grid;
44769                 if (region == 'center' && this.active ) {
44770                     cfg.background = false;
44771                 }
44772                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
44773                 
44774                 this.add(region, ret);
44775                 if (cfg.background) {
44776                     ret.on('activate', function(gp) {
44777                         if (!gp.grid.rendered) {
44778                             gp.grid.render();
44779                         }
44780                     });
44781                 } else {
44782                     grid.render();
44783                 }
44784                 break;
44785            
44786                
44787                 
44788                 
44789             default: 
44790                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
44791                 return null;
44792              // GridPanel (grid, cfg)
44793             
44794         }
44795         this.beginUpdate();
44796         // add children..
44797         Roo.each(xitems, function(i)  {
44798             ret.addxtype(i);
44799         });
44800         this.endUpdate();
44801         return ret;
44802         
44803     }
44804 });
44805
44806 /**
44807  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
44808  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
44809  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
44810  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
44811  * <pre><code>
44812 // shorthand
44813 var CP = Roo.ContentPanel;
44814
44815 var layout = Roo.BorderLayout.create({
44816     north: {
44817         initialSize: 25,
44818         titlebar: false,
44819         panels: [new CP("north", "North")]
44820     },
44821     west: {
44822         split:true,
44823         initialSize: 200,
44824         minSize: 175,
44825         maxSize: 400,
44826         titlebar: true,
44827         collapsible: true,
44828         panels: [new CP("west", {title: "West"})]
44829     },
44830     east: {
44831         split:true,
44832         initialSize: 202,
44833         minSize: 175,
44834         maxSize: 400,
44835         titlebar: true,
44836         collapsible: true,
44837         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
44838     },
44839     south: {
44840         split:true,
44841         initialSize: 100,
44842         minSize: 100,
44843         maxSize: 200,
44844         titlebar: true,
44845         collapsible: true,
44846         panels: [new CP("south", {title: "South", closable: true})]
44847     },
44848     center: {
44849         titlebar: true,
44850         autoScroll:true,
44851         resizeTabs: true,
44852         minTabWidth: 50,
44853         preferredTabWidth: 150,
44854         panels: [
44855             new CP("center1", {title: "Close Me", closable: true}),
44856             new CP("center2", {title: "Center Panel", closable: false})
44857         ]
44858     }
44859 }, document.body);
44860
44861 layout.getRegion("center").showPanel("center1");
44862 </code></pre>
44863  * @param config
44864  * @param targetEl
44865  */
44866 Roo.BorderLayout.create = function(config, targetEl){
44867     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44868     layout.beginUpdate();
44869     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44870     for(var j = 0, jlen = regions.length; j < jlen; j++){
44871         var lr = regions[j];
44872         if(layout.regions[lr] && config[lr].panels){
44873             var r = layout.regions[lr];
44874             var ps = config[lr].panels;
44875             layout.addTypedPanels(r, ps);
44876         }
44877     }
44878     layout.endUpdate();
44879     return layout;
44880 };
44881
44882 // private
44883 Roo.BorderLayout.RegionFactory = {
44884     // private
44885     validRegions : ["north","south","east","west","center"],
44886
44887     // private
44888     create : function(target, mgr, config){
44889         target = target.toLowerCase();
44890         if(config.lightweight || config.basic){
44891             return new Roo.BasicLayoutRegion(mgr, config, target);
44892         }
44893         switch(target){
44894             case "north":
44895                 return new Roo.NorthLayoutRegion(mgr, config);
44896             case "south":
44897                 return new Roo.SouthLayoutRegion(mgr, config);
44898             case "east":
44899                 return new Roo.EastLayoutRegion(mgr, config);
44900             case "west":
44901                 return new Roo.WestLayoutRegion(mgr, config);
44902             case "center":
44903                 return new Roo.CenterLayoutRegion(mgr, config);
44904         }
44905         throw 'Layout region "'+target+'" not supported.';
44906     }
44907 };/*
44908  * Based on:
44909  * Ext JS Library 1.1.1
44910  * Copyright(c) 2006-2007, Ext JS, LLC.
44911  *
44912  * Originally Released Under LGPL - original licence link has changed is not relivant.
44913  *
44914  * Fork - LGPL
44915  * <script type="text/javascript">
44916  */
44917  
44918 /**
44919  * @class Roo.BasicLayoutRegion
44920  * @extends Roo.util.Observable
44921  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
44922  * and does not have a titlebar, tabs or any other features. All it does is size and position 
44923  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
44924  */
44925 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
44926     this.mgr = mgr;
44927     this.position  = pos;
44928     this.events = {
44929         /**
44930          * @scope Roo.BasicLayoutRegion
44931          */
44932         
44933         /**
44934          * @event beforeremove
44935          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
44936          * @param {Roo.LayoutRegion} this
44937          * @param {Roo.ContentPanel} panel The panel
44938          * @param {Object} e The cancel event object
44939          */
44940         "beforeremove" : true,
44941         /**
44942          * @event invalidated
44943          * Fires when the layout for this region is changed.
44944          * @param {Roo.LayoutRegion} this
44945          */
44946         "invalidated" : true,
44947         /**
44948          * @event visibilitychange
44949          * Fires when this region is shown or hidden 
44950          * @param {Roo.LayoutRegion} this
44951          * @param {Boolean} visibility true or false
44952          */
44953         "visibilitychange" : true,
44954         /**
44955          * @event paneladded
44956          * Fires when a panel is added. 
44957          * @param {Roo.LayoutRegion} this
44958          * @param {Roo.ContentPanel} panel The panel
44959          */
44960         "paneladded" : true,
44961         /**
44962          * @event panelremoved
44963          * Fires when a panel is removed. 
44964          * @param {Roo.LayoutRegion} this
44965          * @param {Roo.ContentPanel} panel The panel
44966          */
44967         "panelremoved" : true,
44968         /**
44969          * @event collapsed
44970          * Fires when this region is collapsed.
44971          * @param {Roo.LayoutRegion} this
44972          */
44973         "collapsed" : true,
44974         /**
44975          * @event expanded
44976          * Fires when this region is expanded.
44977          * @param {Roo.LayoutRegion} this
44978          */
44979         "expanded" : true,
44980         /**
44981          * @event slideshow
44982          * Fires when this region is slid into view.
44983          * @param {Roo.LayoutRegion} this
44984          */
44985         "slideshow" : true,
44986         /**
44987          * @event slidehide
44988          * Fires when this region slides out of view. 
44989          * @param {Roo.LayoutRegion} this
44990          */
44991         "slidehide" : true,
44992         /**
44993          * @event panelactivated
44994          * Fires when a panel is activated. 
44995          * @param {Roo.LayoutRegion} this
44996          * @param {Roo.ContentPanel} panel The activated panel
44997          */
44998         "panelactivated" : true,
44999         /**
45000          * @event resized
45001          * Fires when the user resizes this region. 
45002          * @param {Roo.LayoutRegion} this
45003          * @param {Number} newSize The new size (width for east/west, height for north/south)
45004          */
45005         "resized" : true
45006     };
45007     /** A collection of panels in this region. @type Roo.util.MixedCollection */
45008     this.panels = new Roo.util.MixedCollection();
45009     this.panels.getKey = this.getPanelId.createDelegate(this);
45010     this.box = null;
45011     this.activePanel = null;
45012     // ensure listeners are added...
45013     
45014     if (config.listeners || config.events) {
45015         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
45016             listeners : config.listeners || {},
45017             events : config.events || {}
45018         });
45019     }
45020     
45021     if(skipConfig !== true){
45022         this.applyConfig(config);
45023     }
45024 };
45025
45026 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
45027     getPanelId : function(p){
45028         return p.getId();
45029     },
45030     
45031     applyConfig : function(config){
45032         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45033         this.config = config;
45034         
45035     },
45036     
45037     /**
45038      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
45039      * the width, for horizontal (north, south) the height.
45040      * @param {Number} newSize The new width or height
45041      */
45042     resizeTo : function(newSize){
45043         var el = this.el ? this.el :
45044                  (this.activePanel ? this.activePanel.getEl() : null);
45045         if(el){
45046             switch(this.position){
45047                 case "east":
45048                 case "west":
45049                     el.setWidth(newSize);
45050                     this.fireEvent("resized", this, newSize);
45051                 break;
45052                 case "north":
45053                 case "south":
45054                     el.setHeight(newSize);
45055                     this.fireEvent("resized", this, newSize);
45056                 break;                
45057             }
45058         }
45059     },
45060     
45061     getBox : function(){
45062         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45063     },
45064     
45065     getMargins : function(){
45066         return this.margins;
45067     },
45068     
45069     updateBox : function(box){
45070         this.box = box;
45071         var el = this.activePanel.getEl();
45072         el.dom.style.left = box.x + "px";
45073         el.dom.style.top = box.y + "px";
45074         this.activePanel.setSize(box.width, box.height);
45075     },
45076     
45077     /**
45078      * Returns the container element for this region.
45079      * @return {Roo.Element}
45080      */
45081     getEl : function(){
45082         return this.activePanel;
45083     },
45084     
45085     /**
45086      * Returns true if this region is currently visible.
45087      * @return {Boolean}
45088      */
45089     isVisible : function(){
45090         return this.activePanel ? true : false;
45091     },
45092     
45093     setActivePanel : function(panel){
45094         panel = this.getPanel(panel);
45095         if(this.activePanel && this.activePanel != panel){
45096             this.activePanel.setActiveState(false);
45097             this.activePanel.getEl().setLeftTop(-10000,-10000);
45098         }
45099         this.activePanel = panel;
45100         panel.setActiveState(true);
45101         if(this.box){
45102             panel.setSize(this.box.width, this.box.height);
45103         }
45104         this.fireEvent("panelactivated", this, panel);
45105         this.fireEvent("invalidated");
45106     },
45107     
45108     /**
45109      * Show the specified panel.
45110      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45111      * @return {Roo.ContentPanel} The shown panel or null
45112      */
45113     showPanel : function(panel){
45114         if(panel = this.getPanel(panel)){
45115             this.setActivePanel(panel);
45116         }
45117         return panel;
45118     },
45119     
45120     /**
45121      * Get the active panel for this region.
45122      * @return {Roo.ContentPanel} The active panel or null
45123      */
45124     getActivePanel : function(){
45125         return this.activePanel;
45126     },
45127     
45128     /**
45129      * Add the passed ContentPanel(s)
45130      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45131      * @return {Roo.ContentPanel} The panel added (if only one was added)
45132      */
45133     add : function(panel){
45134         if(arguments.length > 1){
45135             for(var i = 0, len = arguments.length; i < len; i++) {
45136                 this.add(arguments[i]);
45137             }
45138             return null;
45139         }
45140         if(this.hasPanel(panel)){
45141             this.showPanel(panel);
45142             return panel;
45143         }
45144         var el = panel.getEl();
45145         if(el.dom.parentNode != this.mgr.el.dom){
45146             this.mgr.el.dom.appendChild(el.dom);
45147         }
45148         if(panel.setRegion){
45149             panel.setRegion(this);
45150         }
45151         this.panels.add(panel);
45152         el.setStyle("position", "absolute");
45153         if(!panel.background){
45154             this.setActivePanel(panel);
45155             if(this.config.initialSize && this.panels.getCount()==1){
45156                 this.resizeTo(this.config.initialSize);
45157             }
45158         }
45159         this.fireEvent("paneladded", this, panel);
45160         return panel;
45161     },
45162     
45163     /**
45164      * Returns true if the panel is in this region.
45165      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45166      * @return {Boolean}
45167      */
45168     hasPanel : function(panel){
45169         if(typeof panel == "object"){ // must be panel obj
45170             panel = panel.getId();
45171         }
45172         return this.getPanel(panel) ? true : false;
45173     },
45174     
45175     /**
45176      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45177      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45178      * @param {Boolean} preservePanel Overrides the config preservePanel option
45179      * @return {Roo.ContentPanel} The panel that was removed
45180      */
45181     remove : function(panel, preservePanel){
45182         panel = this.getPanel(panel);
45183         if(!panel){
45184             return null;
45185         }
45186         var e = {};
45187         this.fireEvent("beforeremove", this, panel, e);
45188         if(e.cancel === true){
45189             return null;
45190         }
45191         var panelId = panel.getId();
45192         this.panels.removeKey(panelId);
45193         return panel;
45194     },
45195     
45196     /**
45197      * Returns the panel specified or null if it's not in this region.
45198      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45199      * @return {Roo.ContentPanel}
45200      */
45201     getPanel : function(id){
45202         if(typeof id == "object"){ // must be panel obj
45203             return id;
45204         }
45205         return this.panels.get(id);
45206     },
45207     
45208     /**
45209      * Returns this regions position (north/south/east/west/center).
45210      * @return {String} 
45211      */
45212     getPosition: function(){
45213         return this.position;    
45214     }
45215 });/*
45216  * Based on:
45217  * Ext JS Library 1.1.1
45218  * Copyright(c) 2006-2007, Ext JS, LLC.
45219  *
45220  * Originally Released Under LGPL - original licence link has changed is not relivant.
45221  *
45222  * Fork - LGPL
45223  * <script type="text/javascript">
45224  */
45225  
45226 /**
45227  * @class Roo.LayoutRegion
45228  * @extends Roo.BasicLayoutRegion
45229  * This class represents a region in a layout manager.
45230  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45231  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45232  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45233  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45234  * @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})
45235  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45236  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45237  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45238  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45239  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45240  * @cfg {String}    title           The title for the region (overrides panel titles)
45241  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45242  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45243  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45244  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45245  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45246  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45247  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45248  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45249  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45250  * @cfg {Boolean}   showPin         True to show a pin button
45251  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45252  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45253  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45254  * @cfg {Number}    width           For East/West panels
45255  * @cfg {Number}    height          For North/South panels
45256  * @cfg {Boolean}   split           To show the splitter
45257  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45258  */
45259 Roo.LayoutRegion = function(mgr, config, pos){
45260     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45261     var dh = Roo.DomHelper;
45262     /** This region's container element 
45263     * @type Roo.Element */
45264     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45265     /** This region's title element 
45266     * @type Roo.Element */
45267
45268     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
45269         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
45270         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
45271     ]}, true);
45272     this.titleEl.enableDisplayMode();
45273     /** This region's title text element 
45274     * @type HTMLElement */
45275     this.titleTextEl = this.titleEl.dom.firstChild;
45276     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
45277     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
45278     this.closeBtn.enableDisplayMode();
45279     this.closeBtn.on("click", this.closeClicked, this);
45280     this.closeBtn.hide();
45281
45282     this.createBody(config);
45283     this.visible = true;
45284     this.collapsed = false;
45285
45286     if(config.hideWhenEmpty){
45287         this.hide();
45288         this.on("paneladded", this.validateVisibility, this);
45289         this.on("panelremoved", this.validateVisibility, this);
45290     }
45291     this.applyConfig(config);
45292 };
45293
45294 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
45295
45296     createBody : function(){
45297         /** This region's body element 
45298         * @type Roo.Element */
45299         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
45300     },
45301
45302     applyConfig : function(c){
45303         if(c.collapsible && this.position != "center" && !this.collapsedEl){
45304             var dh = Roo.DomHelper;
45305             if(c.titlebar !== false){
45306                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
45307                 this.collapseBtn.on("click", this.collapse, this);
45308                 this.collapseBtn.enableDisplayMode();
45309
45310                 if(c.showPin === true || this.showPin){
45311                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
45312                     this.stickBtn.enableDisplayMode();
45313                     this.stickBtn.on("click", this.expand, this);
45314                     this.stickBtn.hide();
45315                 }
45316             }
45317             /** This region's collapsed element
45318             * @type Roo.Element */
45319             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
45320                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
45321             ]}, true);
45322             if(c.floatable !== false){
45323                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
45324                this.collapsedEl.on("click", this.collapseClick, this);
45325             }
45326
45327             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
45328                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
45329                    id: "message", unselectable: "on", style:{"float":"left"}});
45330                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
45331              }
45332             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
45333             this.expandBtn.on("click", this.expand, this);
45334         }
45335         if(this.collapseBtn){
45336             this.collapseBtn.setVisible(c.collapsible == true);
45337         }
45338         this.cmargins = c.cmargins || this.cmargins ||
45339                          (this.position == "west" || this.position == "east" ?
45340                              {top: 0, left: 2, right:2, bottom: 0} :
45341                              {top: 2, left: 0, right:0, bottom: 2});
45342         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45343         this.bottomTabs = c.tabPosition != "top";
45344         this.autoScroll = c.autoScroll || false;
45345         if(this.autoScroll){
45346             this.bodyEl.setStyle("overflow", "auto");
45347         }else{
45348             this.bodyEl.setStyle("overflow", "hidden");
45349         }
45350         //if(c.titlebar !== false){
45351             if((!c.titlebar && !c.title) || c.titlebar === false){
45352                 this.titleEl.hide();
45353             }else{
45354                 this.titleEl.show();
45355                 if(c.title){
45356                     this.titleTextEl.innerHTML = c.title;
45357                 }
45358             }
45359         //}
45360         this.duration = c.duration || .30;
45361         this.slideDuration = c.slideDuration || .45;
45362         this.config = c;
45363         if(c.collapsed){
45364             this.collapse(true);
45365         }
45366         if(c.hidden){
45367             this.hide();
45368         }
45369     },
45370     /**
45371      * Returns true if this region is currently visible.
45372      * @return {Boolean}
45373      */
45374     isVisible : function(){
45375         return this.visible;
45376     },
45377
45378     /**
45379      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
45380      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
45381      */
45382     setCollapsedTitle : function(title){
45383         title = title || "&#160;";
45384         if(this.collapsedTitleTextEl){
45385             this.collapsedTitleTextEl.innerHTML = title;
45386         }
45387     },
45388
45389     getBox : function(){
45390         var b;
45391         if(!this.collapsed){
45392             b = this.el.getBox(false, true);
45393         }else{
45394             b = this.collapsedEl.getBox(false, true);
45395         }
45396         return b;
45397     },
45398
45399     getMargins : function(){
45400         return this.collapsed ? this.cmargins : this.margins;
45401     },
45402
45403     highlight : function(){
45404         this.el.addClass("x-layout-panel-dragover");
45405     },
45406
45407     unhighlight : function(){
45408         this.el.removeClass("x-layout-panel-dragover");
45409     },
45410
45411     updateBox : function(box){
45412         this.box = box;
45413         if(!this.collapsed){
45414             this.el.dom.style.left = box.x + "px";
45415             this.el.dom.style.top = box.y + "px";
45416             this.updateBody(box.width, box.height);
45417         }else{
45418             this.collapsedEl.dom.style.left = box.x + "px";
45419             this.collapsedEl.dom.style.top = box.y + "px";
45420             this.collapsedEl.setSize(box.width, box.height);
45421         }
45422         if(this.tabs){
45423             this.tabs.autoSizeTabs();
45424         }
45425     },
45426
45427     updateBody : function(w, h){
45428         if(w !== null){
45429             this.el.setWidth(w);
45430             w -= this.el.getBorderWidth("rl");
45431             if(this.config.adjustments){
45432                 w += this.config.adjustments[0];
45433             }
45434         }
45435         if(h !== null){
45436             this.el.setHeight(h);
45437             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45438             h -= this.el.getBorderWidth("tb");
45439             if(this.config.adjustments){
45440                 h += this.config.adjustments[1];
45441             }
45442             this.bodyEl.setHeight(h);
45443             if(this.tabs){
45444                 h = this.tabs.syncHeight(h);
45445             }
45446         }
45447         if(this.panelSize){
45448             w = w !== null ? w : this.panelSize.width;
45449             h = h !== null ? h : this.panelSize.height;
45450         }
45451         if(this.activePanel){
45452             var el = this.activePanel.getEl();
45453             w = w !== null ? w : el.getWidth();
45454             h = h !== null ? h : el.getHeight();
45455             this.panelSize = {width: w, height: h};
45456             this.activePanel.setSize(w, h);
45457         }
45458         if(Roo.isIE && this.tabs){
45459             this.tabs.el.repaint();
45460         }
45461     },
45462
45463     /**
45464      * Returns the container element for this region.
45465      * @return {Roo.Element}
45466      */
45467     getEl : function(){
45468         return this.el;
45469     },
45470
45471     /**
45472      * Hides this region.
45473      */
45474     hide : function(){
45475         if(!this.collapsed){
45476             this.el.dom.style.left = "-2000px";
45477             this.el.hide();
45478         }else{
45479             this.collapsedEl.dom.style.left = "-2000px";
45480             this.collapsedEl.hide();
45481         }
45482         this.visible = false;
45483         this.fireEvent("visibilitychange", this, false);
45484     },
45485
45486     /**
45487      * Shows this region if it was previously hidden.
45488      */
45489     show : function(){
45490         if(!this.collapsed){
45491             this.el.show();
45492         }else{
45493             this.collapsedEl.show();
45494         }
45495         this.visible = true;
45496         this.fireEvent("visibilitychange", this, true);
45497     },
45498
45499     closeClicked : function(){
45500         if(this.activePanel){
45501             this.remove(this.activePanel);
45502         }
45503     },
45504
45505     collapseClick : function(e){
45506         if(this.isSlid){
45507            e.stopPropagation();
45508            this.slideIn();
45509         }else{
45510            e.stopPropagation();
45511            this.slideOut();
45512         }
45513     },
45514
45515     /**
45516      * Collapses this region.
45517      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45518      */
45519     collapse : function(skipAnim){
45520         if(this.collapsed) return;
45521         this.collapsed = true;
45522         if(this.split){
45523             this.split.el.hide();
45524         }
45525         if(this.config.animate && skipAnim !== true){
45526             this.fireEvent("invalidated", this);
45527             this.animateCollapse();
45528         }else{
45529             this.el.setLocation(-20000,-20000);
45530             this.el.hide();
45531             this.collapsedEl.show();
45532             this.fireEvent("collapsed", this);
45533             this.fireEvent("invalidated", this);
45534         }
45535     },
45536
45537     animateCollapse : function(){
45538         // overridden
45539     },
45540
45541     /**
45542      * Expands this region if it was previously collapsed.
45543      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45544      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45545      */
45546     expand : function(e, skipAnim){
45547         if(e) e.stopPropagation();
45548         if(!this.collapsed || this.el.hasActiveFx()) return;
45549         if(this.isSlid){
45550             this.afterSlideIn();
45551             skipAnim = true;
45552         }
45553         this.collapsed = false;
45554         if(this.config.animate && skipAnim !== true){
45555             this.animateExpand();
45556         }else{
45557             this.el.show();
45558             if(this.split){
45559                 this.split.el.show();
45560             }
45561             this.collapsedEl.setLocation(-2000,-2000);
45562             this.collapsedEl.hide();
45563             this.fireEvent("invalidated", this);
45564             this.fireEvent("expanded", this);
45565         }
45566     },
45567
45568     animateExpand : function(){
45569         // overridden
45570     },
45571
45572     initTabs : function()
45573     {
45574         this.bodyEl.setStyle("overflow", "hidden");
45575         var ts = new Roo.TabPanel(
45576                 this.bodyEl.dom,
45577                 {
45578                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45579                     disableTooltips: this.config.disableTabTips,
45580                     toolbar : this.config.toolbar
45581                 }
45582         );
45583         if(this.config.hideTabs){
45584             ts.stripWrap.setDisplayed(false);
45585         }
45586         this.tabs = ts;
45587         ts.resizeTabs = this.config.resizeTabs === true;
45588         ts.minTabWidth = this.config.minTabWidth || 40;
45589         ts.maxTabWidth = this.config.maxTabWidth || 250;
45590         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45591         ts.monitorResize = false;
45592         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45593         ts.bodyEl.addClass('x-layout-tabs-body');
45594         this.panels.each(this.initPanelAsTab, this);
45595     },
45596
45597     initPanelAsTab : function(panel){
45598         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45599                     this.config.closeOnTab && panel.isClosable());
45600         if(panel.tabTip !== undefined){
45601             ti.setTooltip(panel.tabTip);
45602         }
45603         ti.on("activate", function(){
45604               this.setActivePanel(panel);
45605         }, this);
45606         if(this.config.closeOnTab){
45607             ti.on("beforeclose", function(t, e){
45608                 e.cancel = true;
45609                 this.remove(panel);
45610             }, this);
45611         }
45612         return ti;
45613     },
45614
45615     updatePanelTitle : function(panel, title){
45616         if(this.activePanel == panel){
45617             this.updateTitle(title);
45618         }
45619         if(this.tabs){
45620             var ti = this.tabs.getTab(panel.getEl().id);
45621             ti.setText(title);
45622             if(panel.tabTip !== undefined){
45623                 ti.setTooltip(panel.tabTip);
45624             }
45625         }
45626     },
45627
45628     updateTitle : function(title){
45629         if(this.titleTextEl && !this.config.title){
45630             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45631         }
45632     },
45633
45634     setActivePanel : function(panel){
45635         panel = this.getPanel(panel);
45636         if(this.activePanel && this.activePanel != panel){
45637             this.activePanel.setActiveState(false);
45638         }
45639         this.activePanel = panel;
45640         panel.setActiveState(true);
45641         if(this.panelSize){
45642             panel.setSize(this.panelSize.width, this.panelSize.height);
45643         }
45644         if(this.closeBtn){
45645             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45646         }
45647         this.updateTitle(panel.getTitle());
45648         if(this.tabs){
45649             this.fireEvent("invalidated", this);
45650         }
45651         this.fireEvent("panelactivated", this, panel);
45652     },
45653
45654     /**
45655      * Shows the specified panel.
45656      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45657      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45658      */
45659     showPanel : function(panel){
45660         if(panel = this.getPanel(panel)){
45661             if(this.tabs){
45662                 var tab = this.tabs.getTab(panel.getEl().id);
45663                 if(tab.isHidden()){
45664                     this.tabs.unhideTab(tab.id);
45665                 }
45666                 tab.activate();
45667             }else{
45668                 this.setActivePanel(panel);
45669             }
45670         }
45671         return panel;
45672     },
45673
45674     /**
45675      * Get the active panel for this region.
45676      * @return {Roo.ContentPanel} The active panel or null
45677      */
45678     getActivePanel : function(){
45679         return this.activePanel;
45680     },
45681
45682     validateVisibility : function(){
45683         if(this.panels.getCount() < 1){
45684             this.updateTitle("&#160;");
45685             this.closeBtn.hide();
45686             this.hide();
45687         }else{
45688             if(!this.isVisible()){
45689                 this.show();
45690             }
45691         }
45692     },
45693
45694     /**
45695      * Adds the passed ContentPanel(s) to this region.
45696      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45697      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45698      */
45699     add : function(panel){
45700         if(arguments.length > 1){
45701             for(var i = 0, len = arguments.length; i < len; i++) {
45702                 this.add(arguments[i]);
45703             }
45704             return null;
45705         }
45706         if(this.hasPanel(panel)){
45707             this.showPanel(panel);
45708             return panel;
45709         }
45710         panel.setRegion(this);
45711         this.panels.add(panel);
45712         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
45713             this.bodyEl.dom.appendChild(panel.getEl().dom);
45714             if(panel.background !== true){
45715                 this.setActivePanel(panel);
45716             }
45717             this.fireEvent("paneladded", this, panel);
45718             return panel;
45719         }
45720         if(!this.tabs){
45721             this.initTabs();
45722         }else{
45723             this.initPanelAsTab(panel);
45724         }
45725         if(panel.background !== true){
45726             this.tabs.activate(panel.getEl().id);
45727         }
45728         this.fireEvent("paneladded", this, panel);
45729         return panel;
45730     },
45731
45732     /**
45733      * Hides the tab for the specified panel.
45734      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45735      */
45736     hidePanel : function(panel){
45737         if(this.tabs && (panel = this.getPanel(panel))){
45738             this.tabs.hideTab(panel.getEl().id);
45739         }
45740     },
45741
45742     /**
45743      * Unhides the tab for a previously hidden panel.
45744      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45745      */
45746     unhidePanel : function(panel){
45747         if(this.tabs && (panel = this.getPanel(panel))){
45748             this.tabs.unhideTab(panel.getEl().id);
45749         }
45750     },
45751
45752     clearPanels : function(){
45753         while(this.panels.getCount() > 0){
45754              this.remove(this.panels.first());
45755         }
45756     },
45757
45758     /**
45759      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45760      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45761      * @param {Boolean} preservePanel Overrides the config preservePanel option
45762      * @return {Roo.ContentPanel} The panel that was removed
45763      */
45764     remove : function(panel, preservePanel){
45765         panel = this.getPanel(panel);
45766         if(!panel){
45767             return null;
45768         }
45769         var e = {};
45770         this.fireEvent("beforeremove", this, panel, e);
45771         if(e.cancel === true){
45772             return null;
45773         }
45774         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
45775         var panelId = panel.getId();
45776         this.panels.removeKey(panelId);
45777         if(preservePanel){
45778             document.body.appendChild(panel.getEl().dom);
45779         }
45780         if(this.tabs){
45781             this.tabs.removeTab(panel.getEl().id);
45782         }else if (!preservePanel){
45783             this.bodyEl.dom.removeChild(panel.getEl().dom);
45784         }
45785         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
45786             var p = this.panels.first();
45787             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
45788             tempEl.appendChild(p.getEl().dom);
45789             this.bodyEl.update("");
45790             this.bodyEl.dom.appendChild(p.getEl().dom);
45791             tempEl = null;
45792             this.updateTitle(p.getTitle());
45793             this.tabs = null;
45794             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45795             this.setActivePanel(p);
45796         }
45797         panel.setRegion(null);
45798         if(this.activePanel == panel){
45799             this.activePanel = null;
45800         }
45801         if(this.config.autoDestroy !== false && preservePanel !== true){
45802             try{panel.destroy();}catch(e){}
45803         }
45804         this.fireEvent("panelremoved", this, panel);
45805         return panel;
45806     },
45807
45808     /**
45809      * Returns the TabPanel component used by this region
45810      * @return {Roo.TabPanel}
45811      */
45812     getTabs : function(){
45813         return this.tabs;
45814     },
45815
45816     createTool : function(parentEl, className){
45817         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
45818             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
45819         btn.addClassOnOver("x-layout-tools-button-over");
45820         return btn;
45821     }
45822 });/*
45823  * Based on:
45824  * Ext JS Library 1.1.1
45825  * Copyright(c) 2006-2007, Ext JS, LLC.
45826  *
45827  * Originally Released Under LGPL - original licence link has changed is not relivant.
45828  *
45829  * Fork - LGPL
45830  * <script type="text/javascript">
45831  */
45832  
45833
45834
45835 /**
45836  * @class Roo.SplitLayoutRegion
45837  * @extends Roo.LayoutRegion
45838  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
45839  */
45840 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
45841     this.cursor = cursor;
45842     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
45843 };
45844
45845 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
45846     splitTip : "Drag to resize.",
45847     collapsibleSplitTip : "Drag to resize. Double click to hide.",
45848     useSplitTips : false,
45849
45850     applyConfig : function(config){
45851         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
45852         if(config.split){
45853             if(!this.split){
45854                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
45855                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
45856                 /** The SplitBar for this region 
45857                 * @type Roo.SplitBar */
45858                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
45859                 this.split.on("moved", this.onSplitMove, this);
45860                 this.split.useShim = config.useShim === true;
45861                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45862                 if(this.useSplitTips){
45863                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45864                 }
45865                 if(config.collapsible){
45866                     this.split.el.on("dblclick", this.collapse,  this);
45867                 }
45868             }
45869             if(typeof config.minSize != "undefined"){
45870                 this.split.minSize = config.minSize;
45871             }
45872             if(typeof config.maxSize != "undefined"){
45873                 this.split.maxSize = config.maxSize;
45874             }
45875             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45876                 this.hideSplitter();
45877             }
45878         }
45879     },
45880
45881     getHMaxSize : function(){
45882          var cmax = this.config.maxSize || 10000;
45883          var center = this.mgr.getRegion("center");
45884          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45885     },
45886
45887     getVMaxSize : function(){
45888          var cmax = this.config.maxSize || 10000;
45889          var center = this.mgr.getRegion("center");
45890          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45891     },
45892
45893     onSplitMove : function(split, newSize){
45894         this.fireEvent("resized", this, newSize);
45895     },
45896     
45897     /** 
45898      * Returns the {@link Roo.SplitBar} for this region.
45899      * @return {Roo.SplitBar}
45900      */
45901     getSplitBar : function(){
45902         return this.split;
45903     },
45904     
45905     hide : function(){
45906         this.hideSplitter();
45907         Roo.SplitLayoutRegion.superclass.hide.call(this);
45908     },
45909
45910     hideSplitter : function(){
45911         if(this.split){
45912             this.split.el.setLocation(-2000,-2000);
45913             this.split.el.hide();
45914         }
45915     },
45916
45917     show : function(){
45918         if(this.split){
45919             this.split.el.show();
45920         }
45921         Roo.SplitLayoutRegion.superclass.show.call(this);
45922     },
45923     
45924     beforeSlide: function(){
45925         if(Roo.isGecko){// firefox overflow auto bug workaround
45926             this.bodyEl.clip();
45927             if(this.tabs) this.tabs.bodyEl.clip();
45928             if(this.activePanel){
45929                 this.activePanel.getEl().clip();
45930                 
45931                 if(this.activePanel.beforeSlide){
45932                     this.activePanel.beforeSlide();
45933                 }
45934             }
45935         }
45936     },
45937     
45938     afterSlide : function(){
45939         if(Roo.isGecko){// firefox overflow auto bug workaround
45940             this.bodyEl.unclip();
45941             if(this.tabs) this.tabs.bodyEl.unclip();
45942             if(this.activePanel){
45943                 this.activePanel.getEl().unclip();
45944                 if(this.activePanel.afterSlide){
45945                     this.activePanel.afterSlide();
45946                 }
45947             }
45948         }
45949     },
45950
45951     initAutoHide : function(){
45952         if(this.autoHide !== false){
45953             if(!this.autoHideHd){
45954                 var st = new Roo.util.DelayedTask(this.slideIn, this);
45955                 this.autoHideHd = {
45956                     "mouseout": function(e){
45957                         if(!e.within(this.el, true)){
45958                             st.delay(500);
45959                         }
45960                     },
45961                     "mouseover" : function(e){
45962                         st.cancel();
45963                     },
45964                     scope : this
45965                 };
45966             }
45967             this.el.on(this.autoHideHd);
45968         }
45969     },
45970
45971     clearAutoHide : function(){
45972         if(this.autoHide !== false){
45973             this.el.un("mouseout", this.autoHideHd.mouseout);
45974             this.el.un("mouseover", this.autoHideHd.mouseover);
45975         }
45976     },
45977
45978     clearMonitor : function(){
45979         Roo.get(document).un("click", this.slideInIf, this);
45980     },
45981
45982     // these names are backwards but not changed for compat
45983     slideOut : function(){
45984         if(this.isSlid || this.el.hasActiveFx()){
45985             return;
45986         }
45987         this.isSlid = true;
45988         if(this.collapseBtn){
45989             this.collapseBtn.hide();
45990         }
45991         this.closeBtnState = this.closeBtn.getStyle('display');
45992         this.closeBtn.hide();
45993         if(this.stickBtn){
45994             this.stickBtn.show();
45995         }
45996         this.el.show();
45997         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
45998         this.beforeSlide();
45999         this.el.setStyle("z-index", 10001);
46000         this.el.slideIn(this.getSlideAnchor(), {
46001             callback: function(){
46002                 this.afterSlide();
46003                 this.initAutoHide();
46004                 Roo.get(document).on("click", this.slideInIf, this);
46005                 this.fireEvent("slideshow", this);
46006             },
46007             scope: this,
46008             block: true
46009         });
46010     },
46011
46012     afterSlideIn : function(){
46013         this.clearAutoHide();
46014         this.isSlid = false;
46015         this.clearMonitor();
46016         this.el.setStyle("z-index", "");
46017         if(this.collapseBtn){
46018             this.collapseBtn.show();
46019         }
46020         this.closeBtn.setStyle('display', this.closeBtnState);
46021         if(this.stickBtn){
46022             this.stickBtn.hide();
46023         }
46024         this.fireEvent("slidehide", this);
46025     },
46026
46027     slideIn : function(cb){
46028         if(!this.isSlid || this.el.hasActiveFx()){
46029             Roo.callback(cb);
46030             return;
46031         }
46032         this.isSlid = false;
46033         this.beforeSlide();
46034         this.el.slideOut(this.getSlideAnchor(), {
46035             callback: function(){
46036                 this.el.setLeftTop(-10000, -10000);
46037                 this.afterSlide();
46038                 this.afterSlideIn();
46039                 Roo.callback(cb);
46040             },
46041             scope: this,
46042             block: true
46043         });
46044     },
46045     
46046     slideInIf : function(e){
46047         if(!e.within(this.el)){
46048             this.slideIn();
46049         }
46050     },
46051
46052     animateCollapse : function(){
46053         this.beforeSlide();
46054         this.el.setStyle("z-index", 20000);
46055         var anchor = this.getSlideAnchor();
46056         this.el.slideOut(anchor, {
46057             callback : function(){
46058                 this.el.setStyle("z-index", "");
46059                 this.collapsedEl.slideIn(anchor, {duration:.3});
46060                 this.afterSlide();
46061                 this.el.setLocation(-10000,-10000);
46062                 this.el.hide();
46063                 this.fireEvent("collapsed", this);
46064             },
46065             scope: this,
46066             block: true
46067         });
46068     },
46069
46070     animateExpand : function(){
46071         this.beforeSlide();
46072         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46073         this.el.setStyle("z-index", 20000);
46074         this.collapsedEl.hide({
46075             duration:.1
46076         });
46077         this.el.slideIn(this.getSlideAnchor(), {
46078             callback : function(){
46079                 this.el.setStyle("z-index", "");
46080                 this.afterSlide();
46081                 if(this.split){
46082                     this.split.el.show();
46083                 }
46084                 this.fireEvent("invalidated", this);
46085                 this.fireEvent("expanded", this);
46086             },
46087             scope: this,
46088             block: true
46089         });
46090     },
46091
46092     anchors : {
46093         "west" : "left",
46094         "east" : "right",
46095         "north" : "top",
46096         "south" : "bottom"
46097     },
46098
46099     sanchors : {
46100         "west" : "l",
46101         "east" : "r",
46102         "north" : "t",
46103         "south" : "b"
46104     },
46105
46106     canchors : {
46107         "west" : "tl-tr",
46108         "east" : "tr-tl",
46109         "north" : "tl-bl",
46110         "south" : "bl-tl"
46111     },
46112
46113     getAnchor : function(){
46114         return this.anchors[this.position];
46115     },
46116
46117     getCollapseAnchor : function(){
46118         return this.canchors[this.position];
46119     },
46120
46121     getSlideAnchor : function(){
46122         return this.sanchors[this.position];
46123     },
46124
46125     getAlignAdj : function(){
46126         var cm = this.cmargins;
46127         switch(this.position){
46128             case "west":
46129                 return [0, 0];
46130             break;
46131             case "east":
46132                 return [0, 0];
46133             break;
46134             case "north":
46135                 return [0, 0];
46136             break;
46137             case "south":
46138                 return [0, 0];
46139             break;
46140         }
46141     },
46142
46143     getExpandAdj : function(){
46144         var c = this.collapsedEl, cm = this.cmargins;
46145         switch(this.position){
46146             case "west":
46147                 return [-(cm.right+c.getWidth()+cm.left), 0];
46148             break;
46149             case "east":
46150                 return [cm.right+c.getWidth()+cm.left, 0];
46151             break;
46152             case "north":
46153                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46154             break;
46155             case "south":
46156                 return [0, cm.top+cm.bottom+c.getHeight()];
46157             break;
46158         }
46159     }
46160 });/*
46161  * Based on:
46162  * Ext JS Library 1.1.1
46163  * Copyright(c) 2006-2007, Ext JS, LLC.
46164  *
46165  * Originally Released Under LGPL - original licence link has changed is not relivant.
46166  *
46167  * Fork - LGPL
46168  * <script type="text/javascript">
46169  */
46170 /*
46171  * These classes are private internal classes
46172  */
46173 Roo.CenterLayoutRegion = function(mgr, config){
46174     Roo.LayoutRegion.call(this, mgr, config, "center");
46175     this.visible = true;
46176     this.minWidth = config.minWidth || 20;
46177     this.minHeight = config.minHeight || 20;
46178 };
46179
46180 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46181     hide : function(){
46182         // center panel can't be hidden
46183     },
46184     
46185     show : function(){
46186         // center panel can't be hidden
46187     },
46188     
46189     getMinWidth: function(){
46190         return this.minWidth;
46191     },
46192     
46193     getMinHeight: function(){
46194         return this.minHeight;
46195     }
46196 });
46197
46198
46199 Roo.NorthLayoutRegion = function(mgr, config){
46200     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46201     if(this.split){
46202         this.split.placement = Roo.SplitBar.TOP;
46203         this.split.orientation = Roo.SplitBar.VERTICAL;
46204         this.split.el.addClass("x-layout-split-v");
46205     }
46206     var size = config.initialSize || config.height;
46207     if(typeof size != "undefined"){
46208         this.el.setHeight(size);
46209     }
46210 };
46211 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46212     orientation: Roo.SplitBar.VERTICAL,
46213     getBox : function(){
46214         if(this.collapsed){
46215             return this.collapsedEl.getBox();
46216         }
46217         var box = this.el.getBox();
46218         if(this.split){
46219             box.height += this.split.el.getHeight();
46220         }
46221         return box;
46222     },
46223     
46224     updateBox : function(box){
46225         if(this.split && !this.collapsed){
46226             box.height -= this.split.el.getHeight();
46227             this.split.el.setLeft(box.x);
46228             this.split.el.setTop(box.y+box.height);
46229             this.split.el.setWidth(box.width);
46230         }
46231         if(this.collapsed){
46232             this.updateBody(box.width, null);
46233         }
46234         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46235     }
46236 });
46237
46238 Roo.SouthLayoutRegion = function(mgr, config){
46239     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46240     if(this.split){
46241         this.split.placement = Roo.SplitBar.BOTTOM;
46242         this.split.orientation = Roo.SplitBar.VERTICAL;
46243         this.split.el.addClass("x-layout-split-v");
46244     }
46245     var size = config.initialSize || config.height;
46246     if(typeof size != "undefined"){
46247         this.el.setHeight(size);
46248     }
46249 };
46250 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46251     orientation: Roo.SplitBar.VERTICAL,
46252     getBox : function(){
46253         if(this.collapsed){
46254             return this.collapsedEl.getBox();
46255         }
46256         var box = this.el.getBox();
46257         if(this.split){
46258             var sh = this.split.el.getHeight();
46259             box.height += sh;
46260             box.y -= sh;
46261         }
46262         return box;
46263     },
46264     
46265     updateBox : function(box){
46266         if(this.split && !this.collapsed){
46267             var sh = this.split.el.getHeight();
46268             box.height -= sh;
46269             box.y += sh;
46270             this.split.el.setLeft(box.x);
46271             this.split.el.setTop(box.y-sh);
46272             this.split.el.setWidth(box.width);
46273         }
46274         if(this.collapsed){
46275             this.updateBody(box.width, null);
46276         }
46277         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46278     }
46279 });
46280
46281 Roo.EastLayoutRegion = function(mgr, config){
46282     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
46283     if(this.split){
46284         this.split.placement = Roo.SplitBar.RIGHT;
46285         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46286         this.split.el.addClass("x-layout-split-h");
46287     }
46288     var size = config.initialSize || config.width;
46289     if(typeof size != "undefined"){
46290         this.el.setWidth(size);
46291     }
46292 };
46293 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
46294     orientation: Roo.SplitBar.HORIZONTAL,
46295     getBox : function(){
46296         if(this.collapsed){
46297             return this.collapsedEl.getBox();
46298         }
46299         var box = this.el.getBox();
46300         if(this.split){
46301             var sw = this.split.el.getWidth();
46302             box.width += sw;
46303             box.x -= sw;
46304         }
46305         return box;
46306     },
46307
46308     updateBox : function(box){
46309         if(this.split && !this.collapsed){
46310             var sw = this.split.el.getWidth();
46311             box.width -= sw;
46312             this.split.el.setLeft(box.x);
46313             this.split.el.setTop(box.y);
46314             this.split.el.setHeight(box.height);
46315             box.x += sw;
46316         }
46317         if(this.collapsed){
46318             this.updateBody(null, box.height);
46319         }
46320         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46321     }
46322 });
46323
46324 Roo.WestLayoutRegion = function(mgr, config){
46325     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
46326     if(this.split){
46327         this.split.placement = Roo.SplitBar.LEFT;
46328         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46329         this.split.el.addClass("x-layout-split-h");
46330     }
46331     var size = config.initialSize || config.width;
46332     if(typeof size != "undefined"){
46333         this.el.setWidth(size);
46334     }
46335 };
46336 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
46337     orientation: Roo.SplitBar.HORIZONTAL,
46338     getBox : function(){
46339         if(this.collapsed){
46340             return this.collapsedEl.getBox();
46341         }
46342         var box = this.el.getBox();
46343         if(this.split){
46344             box.width += this.split.el.getWidth();
46345         }
46346         return box;
46347     },
46348     
46349     updateBox : function(box){
46350         if(this.split && !this.collapsed){
46351             var sw = this.split.el.getWidth();
46352             box.width -= sw;
46353             this.split.el.setLeft(box.x+box.width);
46354             this.split.el.setTop(box.y);
46355             this.split.el.setHeight(box.height);
46356         }
46357         if(this.collapsed){
46358             this.updateBody(null, box.height);
46359         }
46360         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46361     }
46362 });
46363 /*
46364  * Based on:
46365  * Ext JS Library 1.1.1
46366  * Copyright(c) 2006-2007, Ext JS, LLC.
46367  *
46368  * Originally Released Under LGPL - original licence link has changed is not relivant.
46369  *
46370  * Fork - LGPL
46371  * <script type="text/javascript">
46372  */
46373  
46374  
46375 /*
46376  * Private internal class for reading and applying state
46377  */
46378 Roo.LayoutStateManager = function(layout){
46379      // default empty state
46380      this.state = {
46381         north: {},
46382         south: {},
46383         east: {},
46384         west: {}       
46385     };
46386 };
46387
46388 Roo.LayoutStateManager.prototype = {
46389     init : function(layout, provider){
46390         this.provider = provider;
46391         var state = provider.get(layout.id+"-layout-state");
46392         if(state){
46393             var wasUpdating = layout.isUpdating();
46394             if(!wasUpdating){
46395                 layout.beginUpdate();
46396             }
46397             for(var key in state){
46398                 if(typeof state[key] != "function"){
46399                     var rstate = state[key];
46400                     var r = layout.getRegion(key);
46401                     if(r && rstate){
46402                         if(rstate.size){
46403                             r.resizeTo(rstate.size);
46404                         }
46405                         if(rstate.collapsed == true){
46406                             r.collapse(true);
46407                         }else{
46408                             r.expand(null, true);
46409                         }
46410                     }
46411                 }
46412             }
46413             if(!wasUpdating){
46414                 layout.endUpdate();
46415             }
46416             this.state = state; 
46417         }
46418         this.layout = layout;
46419         layout.on("regionresized", this.onRegionResized, this);
46420         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46421         layout.on("regionexpanded", this.onRegionExpanded, this);
46422     },
46423     
46424     storeState : function(){
46425         this.provider.set(this.layout.id+"-layout-state", this.state);
46426     },
46427     
46428     onRegionResized : function(region, newSize){
46429         this.state[region.getPosition()].size = newSize;
46430         this.storeState();
46431     },
46432     
46433     onRegionCollapsed : function(region){
46434         this.state[region.getPosition()].collapsed = true;
46435         this.storeState();
46436     },
46437     
46438     onRegionExpanded : function(region){
46439         this.state[region.getPosition()].collapsed = false;
46440         this.storeState();
46441     }
46442 };/*
46443  * Based on:
46444  * Ext JS Library 1.1.1
46445  * Copyright(c) 2006-2007, Ext JS, LLC.
46446  *
46447  * Originally Released Under LGPL - original licence link has changed is not relivant.
46448  *
46449  * Fork - LGPL
46450  * <script type="text/javascript">
46451  */
46452 /**
46453  * @class Roo.ContentPanel
46454  * @extends Roo.util.Observable
46455  * A basic ContentPanel element.
46456  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46457  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46458  * @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
46459  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46460  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46461  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46462  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46463  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46464  * @cfg {String} title          The title for this panel
46465  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46466  * @cfg {String} url            Calls {@link #setUrl} with this value
46467  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46468  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46469  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46470  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46471
46472  * @constructor
46473  * Create a new ContentPanel.
46474  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46475  * @param {String/Object} config A string to set only the title or a config object
46476  * @param {String} content (optional) Set the HTML content for this panel
46477  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46478  */
46479 Roo.ContentPanel = function(el, config, content){
46480     
46481      
46482     /*
46483     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46484         config = el;
46485         el = Roo.id();
46486     }
46487     if (config && config.parentLayout) { 
46488         el = config.parentLayout.el.createChild(); 
46489     }
46490     */
46491     if(el.autoCreate){ // xtype is available if this is called from factory
46492         config = el;
46493         el = Roo.id();
46494     }
46495     this.el = Roo.get(el);
46496     if(!this.el && config && config.autoCreate){
46497         if(typeof config.autoCreate == "object"){
46498             if(!config.autoCreate.id){
46499                 config.autoCreate.id = config.id||el;
46500             }
46501             this.el = Roo.DomHelper.append(document.body,
46502                         config.autoCreate, true);
46503         }else{
46504             this.el = Roo.DomHelper.append(document.body,
46505                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46506         }
46507     }
46508     this.closable = false;
46509     this.loaded = false;
46510     this.active = false;
46511     if(typeof config == "string"){
46512         this.title = config;
46513     }else{
46514         Roo.apply(this, config);
46515     }
46516     
46517     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46518         this.wrapEl = this.el.wrap();
46519         this.toolbar.container = this.el.insertSibling(false, 'before');
46520         this.toolbar = new Roo.Toolbar(this.toolbar);
46521     }
46522     
46523     
46524     
46525     if(this.resizeEl){
46526         this.resizeEl = Roo.get(this.resizeEl, true);
46527     }else{
46528         this.resizeEl = this.el;
46529     }
46530     this.addEvents({
46531         /**
46532          * @event activate
46533          * Fires when this panel is activated. 
46534          * @param {Roo.ContentPanel} this
46535          */
46536         "activate" : true,
46537         /**
46538          * @event deactivate
46539          * Fires when this panel is activated. 
46540          * @param {Roo.ContentPanel} this
46541          */
46542         "deactivate" : true,
46543
46544         /**
46545          * @event resize
46546          * Fires when this panel is resized if fitToFrame is true.
46547          * @param {Roo.ContentPanel} this
46548          * @param {Number} width The width after any component adjustments
46549          * @param {Number} height The height after any component adjustments
46550          */
46551         "resize" : true,
46552         
46553          /**
46554          * @event render
46555          * Fires when this tab is created
46556          * @param {Roo.ContentPanel} this
46557          */
46558         "render" : true
46559         
46560         
46561         
46562     });
46563     if(this.autoScroll){
46564         this.resizeEl.setStyle("overflow", "auto");
46565     } else {
46566         // fix randome scrolling
46567         this.el.on('scroll', function() {
46568             Roo.log('fix random scolling');
46569             this.scrollTo('top',0); 
46570         });
46571     }
46572     content = content || this.content;
46573     if(content){
46574         this.setContent(content);
46575     }
46576     if(config && config.url){
46577         this.setUrl(this.url, this.params, this.loadOnce);
46578     }
46579     
46580     
46581     
46582     Roo.ContentPanel.superclass.constructor.call(this);
46583     
46584     this.fireEvent('render', this);
46585 };
46586
46587 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46588     tabTip:'',
46589     setRegion : function(region){
46590         this.region = region;
46591         if(region){
46592            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46593         }else{
46594            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46595         } 
46596     },
46597     
46598     /**
46599      * Returns the toolbar for this Panel if one was configured. 
46600      * @return {Roo.Toolbar} 
46601      */
46602     getToolbar : function(){
46603         return this.toolbar;
46604     },
46605     
46606     setActiveState : function(active){
46607         this.active = active;
46608         if(!active){
46609             this.fireEvent("deactivate", this);
46610         }else{
46611             this.fireEvent("activate", this);
46612         }
46613     },
46614     /**
46615      * Updates this panel's element
46616      * @param {String} content The new content
46617      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46618     */
46619     setContent : function(content, loadScripts){
46620         this.el.update(content, loadScripts);
46621     },
46622
46623     ignoreResize : function(w, h){
46624         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46625             return true;
46626         }else{
46627             this.lastSize = {width: w, height: h};
46628             return false;
46629         }
46630     },
46631     /**
46632      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46633      * @return {Roo.UpdateManager} The UpdateManager
46634      */
46635     getUpdateManager : function(){
46636         return this.el.getUpdateManager();
46637     },
46638      /**
46639      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46640      * @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:
46641 <pre><code>
46642 panel.load({
46643     url: "your-url.php",
46644     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46645     callback: yourFunction,
46646     scope: yourObject, //(optional scope)
46647     discardUrl: false,
46648     nocache: false,
46649     text: "Loading...",
46650     timeout: 30,
46651     scripts: false
46652 });
46653 </code></pre>
46654      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46655      * 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.
46656      * @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}
46657      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46658      * @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.
46659      * @return {Roo.ContentPanel} this
46660      */
46661     load : function(){
46662         var um = this.el.getUpdateManager();
46663         um.update.apply(um, arguments);
46664         return this;
46665     },
46666
46667
46668     /**
46669      * 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.
46670      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46671      * @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)
46672      * @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)
46673      * @return {Roo.UpdateManager} The UpdateManager
46674      */
46675     setUrl : function(url, params, loadOnce){
46676         if(this.refreshDelegate){
46677             this.removeListener("activate", this.refreshDelegate);
46678         }
46679         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46680         this.on("activate", this.refreshDelegate);
46681         return this.el.getUpdateManager();
46682     },
46683     
46684     _handleRefresh : function(url, params, loadOnce){
46685         if(!loadOnce || !this.loaded){
46686             var updater = this.el.getUpdateManager();
46687             updater.update(url, params, this._setLoaded.createDelegate(this));
46688         }
46689     },
46690     
46691     _setLoaded : function(){
46692         this.loaded = true;
46693     }, 
46694     
46695     /**
46696      * Returns this panel's id
46697      * @return {String} 
46698      */
46699     getId : function(){
46700         return this.el.id;
46701     },
46702     
46703     /** 
46704      * Returns this panel's element - used by regiosn to add.
46705      * @return {Roo.Element} 
46706      */
46707     getEl : function(){
46708         return this.wrapEl || this.el;
46709     },
46710     
46711     adjustForComponents : function(width, height){
46712         if(this.resizeEl != this.el){
46713             width -= this.el.getFrameWidth('lr');
46714             height -= this.el.getFrameWidth('tb');
46715         }
46716         if(this.toolbar){
46717             var te = this.toolbar.getEl();
46718             height -= te.getHeight();
46719             te.setWidth(width);
46720         }
46721         if(this.adjustments){
46722             width += this.adjustments[0];
46723             height += this.adjustments[1];
46724         }
46725         return {"width": width, "height": height};
46726     },
46727     
46728     setSize : function(width, height){
46729         if(this.fitToFrame && !this.ignoreResize(width, height)){
46730             if(this.fitContainer && this.resizeEl != this.el){
46731                 this.el.setSize(width, height);
46732             }
46733             var size = this.adjustForComponents(width, height);
46734             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
46735             this.fireEvent('resize', this, size.width, size.height);
46736         }
46737     },
46738     
46739     /**
46740      * Returns this panel's title
46741      * @return {String} 
46742      */
46743     getTitle : function(){
46744         return this.title;
46745     },
46746     
46747     /**
46748      * Set this panel's title
46749      * @param {String} title
46750      */
46751     setTitle : function(title){
46752         this.title = title;
46753         if(this.region){
46754             this.region.updatePanelTitle(this, title);
46755         }
46756     },
46757     
46758     /**
46759      * Returns true is this panel was configured to be closable
46760      * @return {Boolean} 
46761      */
46762     isClosable : function(){
46763         return this.closable;
46764     },
46765     
46766     beforeSlide : function(){
46767         this.el.clip();
46768         this.resizeEl.clip();
46769     },
46770     
46771     afterSlide : function(){
46772         this.el.unclip();
46773         this.resizeEl.unclip();
46774     },
46775     
46776     /**
46777      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
46778      *   Will fail silently if the {@link #setUrl} method has not been called.
46779      *   This does not activate the panel, just updates its content.
46780      */
46781     refresh : function(){
46782         if(this.refreshDelegate){
46783            this.loaded = false;
46784            this.refreshDelegate();
46785         }
46786     },
46787     
46788     /**
46789      * Destroys this panel
46790      */
46791     destroy : function(){
46792         this.el.removeAllListeners();
46793         var tempEl = document.createElement("span");
46794         tempEl.appendChild(this.el.dom);
46795         tempEl.innerHTML = "";
46796         this.el.remove();
46797         this.el = null;
46798     },
46799     
46800     /**
46801      * form - if the content panel contains a form - this is a reference to it.
46802      * @type {Roo.form.Form}
46803      */
46804     form : false,
46805     /**
46806      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
46807      *    This contains a reference to it.
46808      * @type {Roo.View}
46809      */
46810     view : false,
46811     
46812       /**
46813      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
46814      * <pre><code>
46815
46816 layout.addxtype({
46817        xtype : 'Form',
46818        items: [ .... ]
46819    }
46820 );
46821
46822 </code></pre>
46823      * @param {Object} cfg Xtype definition of item to add.
46824      */
46825     
46826     addxtype : function(cfg) {
46827         // add form..
46828         if (cfg.xtype.match(/^Form$/)) {
46829             var el = this.el.createChild();
46830
46831             this.form = new  Roo.form.Form(cfg);
46832             
46833             
46834             if ( this.form.allItems.length) this.form.render(el.dom);
46835             return this.form;
46836         }
46837         // should only have one of theses..
46838         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
46839             // views..
46840             cfg.el = this.el.appendChild(document.createElement("div"));
46841             // factory?
46842             
46843             var ret = new Roo.factory(cfg);
46844             ret.render && ret.render(false, ''); // render blank..
46845             this.view = ret;
46846             return ret;
46847         }
46848         return false;
46849     }
46850 });
46851
46852 /**
46853  * @class Roo.GridPanel
46854  * @extends Roo.ContentPanel
46855  * @constructor
46856  * Create a new GridPanel.
46857  * @param {Roo.grid.Grid} grid The grid for this panel
46858  * @param {String/Object} config A string to set only the panel's title, or a config object
46859  */
46860 Roo.GridPanel = function(grid, config){
46861     
46862   
46863     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
46864         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
46865         
46866     this.wrapper.dom.appendChild(grid.getGridEl().dom);
46867     
46868     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
46869     
46870     if(this.toolbar){
46871         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
46872     }
46873     // xtype created footer. - not sure if will work as we normally have to render first..
46874     if (this.footer && !this.footer.el && this.footer.xtype) {
46875         
46876         this.footer.container = this.grid.getView().getFooterPanel(true);
46877         this.footer.dataSource = this.grid.dataSource;
46878         this.footer = Roo.factory(this.footer, Roo);
46879         
46880     }
46881     
46882     grid.monitorWindowResize = false; // turn off autosizing
46883     grid.autoHeight = false;
46884     grid.autoWidth = false;
46885     this.grid = grid;
46886     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
46887 };
46888
46889 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
46890     getId : function(){
46891         return this.grid.id;
46892     },
46893     
46894     /**
46895      * Returns the grid for this panel
46896      * @return {Roo.grid.Grid} 
46897      */
46898     getGrid : function(){
46899         return this.grid;    
46900     },
46901     
46902     setSize : function(width, height){
46903         if(!this.ignoreResize(width, height)){
46904             var grid = this.grid;
46905             var size = this.adjustForComponents(width, height);
46906             grid.getGridEl().setSize(size.width, size.height);
46907             grid.autoSize();
46908         }
46909     },
46910     
46911     beforeSlide : function(){
46912         this.grid.getView().scroller.clip();
46913     },
46914     
46915     afterSlide : function(){
46916         this.grid.getView().scroller.unclip();
46917     },
46918     
46919     destroy : function(){
46920         this.grid.destroy();
46921         delete this.grid;
46922         Roo.GridPanel.superclass.destroy.call(this); 
46923     }
46924 });
46925
46926
46927 /**
46928  * @class Roo.NestedLayoutPanel
46929  * @extends Roo.ContentPanel
46930  * @constructor
46931  * Create a new NestedLayoutPanel.
46932  * 
46933  * 
46934  * @param {Roo.BorderLayout} layout The layout for this panel
46935  * @param {String/Object} config A string to set only the title or a config object
46936  */
46937 Roo.NestedLayoutPanel = function(layout, config)
46938 {
46939     // construct with only one argument..
46940     /* FIXME - implement nicer consturctors
46941     if (layout.layout) {
46942         config = layout;
46943         layout = config.layout;
46944         delete config.layout;
46945     }
46946     if (layout.xtype && !layout.getEl) {
46947         // then layout needs constructing..
46948         layout = Roo.factory(layout, Roo);
46949     }
46950     */
46951     
46952     
46953     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
46954     
46955     layout.monitorWindowResize = false; // turn off autosizing
46956     this.layout = layout;
46957     this.layout.getEl().addClass("x-layout-nested-layout");
46958     
46959     
46960     
46961     
46962 };
46963
46964 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
46965
46966     setSize : function(width, height){
46967         if(!this.ignoreResize(width, height)){
46968             var size = this.adjustForComponents(width, height);
46969             var el = this.layout.getEl();
46970             el.setSize(size.width, size.height);
46971             var touch = el.dom.offsetWidth;
46972             this.layout.layout();
46973             // ie requires a double layout on the first pass
46974             if(Roo.isIE && !this.initialized){
46975                 this.initialized = true;
46976                 this.layout.layout();
46977             }
46978         }
46979     },
46980     
46981     // activate all subpanels if not currently active..
46982     
46983     setActiveState : function(active){
46984         this.active = active;
46985         if(!active){
46986             this.fireEvent("deactivate", this);
46987             return;
46988         }
46989         
46990         this.fireEvent("activate", this);
46991         // not sure if this should happen before or after..
46992         if (!this.layout) {
46993             return; // should not happen..
46994         }
46995         var reg = false;
46996         for (var r in this.layout.regions) {
46997             reg = this.layout.getRegion(r);
46998             if (reg.getActivePanel()) {
46999                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
47000                 reg.setActivePanel(reg.getActivePanel());
47001                 continue;
47002             }
47003             if (!reg.panels.length) {
47004                 continue;
47005             }
47006             reg.showPanel(reg.getPanel(0));
47007         }
47008         
47009         
47010         
47011         
47012     },
47013     
47014     /**
47015      * Returns the nested BorderLayout for this panel
47016      * @return {Roo.BorderLayout} 
47017      */
47018     getLayout : function(){
47019         return this.layout;
47020     },
47021     
47022      /**
47023      * Adds a xtype elements to the layout of the nested panel
47024      * <pre><code>
47025
47026 panel.addxtype({
47027        xtype : 'ContentPanel',
47028        region: 'west',
47029        items: [ .... ]
47030    }
47031 );
47032
47033 panel.addxtype({
47034         xtype : 'NestedLayoutPanel',
47035         region: 'west',
47036         layout: {
47037            center: { },
47038            west: { }   
47039         },
47040         items : [ ... list of content panels or nested layout panels.. ]
47041    }
47042 );
47043 </code></pre>
47044      * @param {Object} cfg Xtype definition of item to add.
47045      */
47046     addxtype : function(cfg) {
47047         return this.layout.addxtype(cfg);
47048     
47049     }
47050 });
47051
47052 Roo.ScrollPanel = function(el, config, content){
47053     config = config || {};
47054     config.fitToFrame = true;
47055     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
47056     
47057     this.el.dom.style.overflow = "hidden";
47058     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
47059     this.el.removeClass("x-layout-inactive-content");
47060     this.el.on("mousewheel", this.onWheel, this);
47061
47062     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47063     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47064     up.unselectable(); down.unselectable();
47065     up.on("click", this.scrollUp, this);
47066     down.on("click", this.scrollDown, this);
47067     up.addClassOnOver("x-scroller-btn-over");
47068     down.addClassOnOver("x-scroller-btn-over");
47069     up.addClassOnClick("x-scroller-btn-click");
47070     down.addClassOnClick("x-scroller-btn-click");
47071     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47072
47073     this.resizeEl = this.el;
47074     this.el = wrap; this.up = up; this.down = down;
47075 };
47076
47077 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47078     increment : 100,
47079     wheelIncrement : 5,
47080     scrollUp : function(){
47081         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47082     },
47083
47084     scrollDown : function(){
47085         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47086     },
47087
47088     afterScroll : function(){
47089         var el = this.resizeEl;
47090         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47091         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47092         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47093     },
47094
47095     setSize : function(){
47096         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47097         this.afterScroll();
47098     },
47099
47100     onWheel : function(e){
47101         var d = e.getWheelDelta();
47102         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47103         this.afterScroll();
47104         e.stopEvent();
47105     },
47106
47107     setContent : function(content, loadScripts){
47108         this.resizeEl.update(content, loadScripts);
47109     }
47110
47111 });
47112
47113
47114
47115
47116
47117
47118
47119
47120
47121 /**
47122  * @class Roo.TreePanel
47123  * @extends Roo.ContentPanel
47124  * @constructor
47125  * Create a new TreePanel. - defaults to fit/scoll contents.
47126  * @param {String/Object} config A string to set only the panel's title, or a config object
47127  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47128  */
47129 Roo.TreePanel = function(config){
47130     var el = config.el;
47131     var tree = config.tree;
47132     delete config.tree; 
47133     delete config.el; // hopefull!
47134     
47135     // wrapper for IE7 strict & safari scroll issue
47136     
47137     var treeEl = el.createChild();
47138     config.resizeEl = treeEl;
47139     
47140     
47141     
47142     Roo.TreePanel.superclass.constructor.call(this, el, config);
47143  
47144  
47145     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47146     //console.log(tree);
47147     this.on('activate', function()
47148     {
47149         if (this.tree.rendered) {
47150             return;
47151         }
47152         //console.log('render tree');
47153         this.tree.render();
47154     });
47155     
47156     this.on('resize',  function (cp, w, h) {
47157             this.tree.innerCt.setWidth(w);
47158             this.tree.innerCt.setHeight(h);
47159             this.tree.innerCt.setStyle('overflow-y', 'auto');
47160     });
47161
47162         
47163     
47164 };
47165
47166 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47167     fitToFrame : true,
47168     autoScroll : true
47169 });
47170
47171
47172
47173
47174
47175
47176
47177
47178
47179
47180
47181 /*
47182  * Based on:
47183  * Ext JS Library 1.1.1
47184  * Copyright(c) 2006-2007, Ext JS, LLC.
47185  *
47186  * Originally Released Under LGPL - original licence link has changed is not relivant.
47187  *
47188  * Fork - LGPL
47189  * <script type="text/javascript">
47190  */
47191  
47192
47193 /**
47194  * @class Roo.ReaderLayout
47195  * @extends Roo.BorderLayout
47196  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47197  * center region containing two nested regions (a top one for a list view and one for item preview below),
47198  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47199  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47200  * expedites the setup of the overall layout and regions for this common application style.
47201  * Example:
47202  <pre><code>
47203 var reader = new Roo.ReaderLayout();
47204 var CP = Roo.ContentPanel;  // shortcut for adding
47205
47206 reader.beginUpdate();
47207 reader.add("north", new CP("north", "North"));
47208 reader.add("west", new CP("west", {title: "West"}));
47209 reader.add("east", new CP("east", {title: "East"}));
47210
47211 reader.regions.listView.add(new CP("listView", "List"));
47212 reader.regions.preview.add(new CP("preview", "Preview"));
47213 reader.endUpdate();
47214 </code></pre>
47215 * @constructor
47216 * Create a new ReaderLayout
47217 * @param {Object} config Configuration options
47218 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47219 * document.body if omitted)
47220 */
47221 Roo.ReaderLayout = function(config, renderTo){
47222     var c = config || {size:{}};
47223     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47224         north: c.north !== false ? Roo.apply({
47225             split:false,
47226             initialSize: 32,
47227             titlebar: false
47228         }, c.north) : false,
47229         west: c.west !== false ? Roo.apply({
47230             split:true,
47231             initialSize: 200,
47232             minSize: 175,
47233             maxSize: 400,
47234             titlebar: true,
47235             collapsible: true,
47236             animate: true,
47237             margins:{left:5,right:0,bottom:5,top:5},
47238             cmargins:{left:5,right:5,bottom:5,top:5}
47239         }, c.west) : false,
47240         east: c.east !== false ? Roo.apply({
47241             split:true,
47242             initialSize: 200,
47243             minSize: 175,
47244             maxSize: 400,
47245             titlebar: true,
47246             collapsible: true,
47247             animate: true,
47248             margins:{left:0,right:5,bottom:5,top:5},
47249             cmargins:{left:5,right:5,bottom:5,top:5}
47250         }, c.east) : false,
47251         center: Roo.apply({
47252             tabPosition: 'top',
47253             autoScroll:false,
47254             closeOnTab: true,
47255             titlebar:false,
47256             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47257         }, c.center)
47258     });
47259
47260     this.el.addClass('x-reader');
47261
47262     this.beginUpdate();
47263
47264     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47265         south: c.preview !== false ? Roo.apply({
47266             split:true,
47267             initialSize: 200,
47268             minSize: 100,
47269             autoScroll:true,
47270             collapsible:true,
47271             titlebar: true,
47272             cmargins:{top:5,left:0, right:0, bottom:0}
47273         }, c.preview) : false,
47274         center: Roo.apply({
47275             autoScroll:false,
47276             titlebar:false,
47277             minHeight:200
47278         }, c.listView)
47279     });
47280     this.add('center', new Roo.NestedLayoutPanel(inner,
47281             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
47282
47283     this.endUpdate();
47284
47285     this.regions.preview = inner.getRegion('south');
47286     this.regions.listView = inner.getRegion('center');
47287 };
47288
47289 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
47290  * Based on:
47291  * Ext JS Library 1.1.1
47292  * Copyright(c) 2006-2007, Ext JS, LLC.
47293  *
47294  * Originally Released Under LGPL - original licence link has changed is not relivant.
47295  *
47296  * Fork - LGPL
47297  * <script type="text/javascript">
47298  */
47299  
47300 /**
47301  * @class Roo.grid.Grid
47302  * @extends Roo.util.Observable
47303  * This class represents the primary interface of a component based grid control.
47304  * <br><br>Usage:<pre><code>
47305  var grid = new Roo.grid.Grid("my-container-id", {
47306      ds: myDataStore,
47307      cm: myColModel,
47308      selModel: mySelectionModel,
47309      autoSizeColumns: true,
47310      monitorWindowResize: false,
47311      trackMouseOver: true
47312  });
47313  // set any options
47314  grid.render();
47315  * </code></pre>
47316  * <b>Common Problems:</b><br/>
47317  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
47318  * element will correct this<br/>
47319  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
47320  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
47321  * are unpredictable.<br/>
47322  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
47323  * grid to calculate dimensions/offsets.<br/>
47324   * @constructor
47325  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
47326  * The container MUST have some type of size defined for the grid to fill. The container will be
47327  * automatically set to position relative if it isn't already.
47328  * @param {Object} config A config object that sets properties on this grid.
47329  */
47330 Roo.grid.Grid = function(container, config){
47331         // initialize the container
47332         this.container = Roo.get(container);
47333         this.container.update("");
47334         this.container.setStyle("overflow", "hidden");
47335     this.container.addClass('x-grid-container');
47336
47337     this.id = this.container.id;
47338
47339     Roo.apply(this, config);
47340     // check and correct shorthanded configs
47341     if(this.ds){
47342         this.dataSource = this.ds;
47343         delete this.ds;
47344     }
47345     if(this.cm){
47346         this.colModel = this.cm;
47347         delete this.cm;
47348     }
47349     if(this.sm){
47350         this.selModel = this.sm;
47351         delete this.sm;
47352     }
47353
47354     if (this.selModel) {
47355         this.selModel = Roo.factory(this.selModel, Roo.grid);
47356         this.sm = this.selModel;
47357         this.sm.xmodule = this.xmodule || false;
47358     }
47359     if (typeof(this.colModel.config) == 'undefined') {
47360         this.colModel = new Roo.grid.ColumnModel(this.colModel);
47361         this.cm = this.colModel;
47362         this.cm.xmodule = this.xmodule || false;
47363     }
47364     if (this.dataSource) {
47365         this.dataSource= Roo.factory(this.dataSource, Roo.data);
47366         this.ds = this.dataSource;
47367         this.ds.xmodule = this.xmodule || false;
47368          
47369     }
47370     
47371     
47372     
47373     if(this.width){
47374         this.container.setWidth(this.width);
47375     }
47376
47377     if(this.height){
47378         this.container.setHeight(this.height);
47379     }
47380     /** @private */
47381         this.addEvents({
47382         // raw events
47383         /**
47384          * @event click
47385          * The raw click event for the entire grid.
47386          * @param {Roo.EventObject} e
47387          */
47388         "click" : true,
47389         /**
47390          * @event dblclick
47391          * The raw dblclick event for the entire grid.
47392          * @param {Roo.EventObject} e
47393          */
47394         "dblclick" : true,
47395         /**
47396          * @event contextmenu
47397          * The raw contextmenu event for the entire grid.
47398          * @param {Roo.EventObject} e
47399          */
47400         "contextmenu" : true,
47401         /**
47402          * @event mousedown
47403          * The raw mousedown event for the entire grid.
47404          * @param {Roo.EventObject} e
47405          */
47406         "mousedown" : true,
47407         /**
47408          * @event mouseup
47409          * The raw mouseup event for the entire grid.
47410          * @param {Roo.EventObject} e
47411          */
47412         "mouseup" : true,
47413         /**
47414          * @event mouseover
47415          * The raw mouseover event for the entire grid.
47416          * @param {Roo.EventObject} e
47417          */
47418         "mouseover" : true,
47419         /**
47420          * @event mouseout
47421          * The raw mouseout event for the entire grid.
47422          * @param {Roo.EventObject} e
47423          */
47424         "mouseout" : true,
47425         /**
47426          * @event keypress
47427          * The raw keypress event for the entire grid.
47428          * @param {Roo.EventObject} e
47429          */
47430         "keypress" : true,
47431         /**
47432          * @event keydown
47433          * The raw keydown event for the entire grid.
47434          * @param {Roo.EventObject} e
47435          */
47436         "keydown" : true,
47437
47438         // custom events
47439
47440         /**
47441          * @event cellclick
47442          * Fires when a cell is clicked
47443          * @param {Grid} this
47444          * @param {Number} rowIndex
47445          * @param {Number} columnIndex
47446          * @param {Roo.EventObject} e
47447          */
47448         "cellclick" : true,
47449         /**
47450          * @event celldblclick
47451          * Fires when a cell is double clicked
47452          * @param {Grid} this
47453          * @param {Number} rowIndex
47454          * @param {Number} columnIndex
47455          * @param {Roo.EventObject} e
47456          */
47457         "celldblclick" : true,
47458         /**
47459          * @event rowclick
47460          * Fires when a row is clicked
47461          * @param {Grid} this
47462          * @param {Number} rowIndex
47463          * @param {Roo.EventObject} e
47464          */
47465         "rowclick" : true,
47466         /**
47467          * @event rowdblclick
47468          * Fires when a row is double clicked
47469          * @param {Grid} this
47470          * @param {Number} rowIndex
47471          * @param {Roo.EventObject} e
47472          */
47473         "rowdblclick" : true,
47474         /**
47475          * @event headerclick
47476          * Fires when a header is clicked
47477          * @param {Grid} this
47478          * @param {Number} columnIndex
47479          * @param {Roo.EventObject} e
47480          */
47481         "headerclick" : true,
47482         /**
47483          * @event headerdblclick
47484          * Fires when a header cell is double clicked
47485          * @param {Grid} this
47486          * @param {Number} columnIndex
47487          * @param {Roo.EventObject} e
47488          */
47489         "headerdblclick" : true,
47490         /**
47491          * @event rowcontextmenu
47492          * Fires when a row is right clicked
47493          * @param {Grid} this
47494          * @param {Number} rowIndex
47495          * @param {Roo.EventObject} e
47496          */
47497         "rowcontextmenu" : true,
47498         /**
47499          * @event cellcontextmenu
47500          * Fires when a cell is right clicked
47501          * @param {Grid} this
47502          * @param {Number} rowIndex
47503          * @param {Number} cellIndex
47504          * @param {Roo.EventObject} e
47505          */
47506          "cellcontextmenu" : true,
47507         /**
47508          * @event headercontextmenu
47509          * Fires when a header is right clicked
47510          * @param {Grid} this
47511          * @param {Number} columnIndex
47512          * @param {Roo.EventObject} e
47513          */
47514         "headercontextmenu" : true,
47515         /**
47516          * @event bodyscroll
47517          * Fires when the body element is scrolled
47518          * @param {Number} scrollLeft
47519          * @param {Number} scrollTop
47520          */
47521         "bodyscroll" : true,
47522         /**
47523          * @event columnresize
47524          * Fires when the user resizes a column
47525          * @param {Number} columnIndex
47526          * @param {Number} newSize
47527          */
47528         "columnresize" : true,
47529         /**
47530          * @event columnmove
47531          * Fires when the user moves a column
47532          * @param {Number} oldIndex
47533          * @param {Number} newIndex
47534          */
47535         "columnmove" : true,
47536         /**
47537          * @event startdrag
47538          * Fires when row(s) start being dragged
47539          * @param {Grid} this
47540          * @param {Roo.GridDD} dd The drag drop object
47541          * @param {event} e The raw browser event
47542          */
47543         "startdrag" : true,
47544         /**
47545          * @event enddrag
47546          * Fires when a drag operation is complete
47547          * @param {Grid} this
47548          * @param {Roo.GridDD} dd The drag drop object
47549          * @param {event} e The raw browser event
47550          */
47551         "enddrag" : true,
47552         /**
47553          * @event dragdrop
47554          * Fires when dragged row(s) are dropped on a valid DD target
47555          * @param {Grid} this
47556          * @param {Roo.GridDD} dd The drag drop object
47557          * @param {String} targetId The target drag drop object
47558          * @param {event} e The raw browser event
47559          */
47560         "dragdrop" : true,
47561         /**
47562          * @event dragover
47563          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
47564          * @param {Grid} this
47565          * @param {Roo.GridDD} dd The drag drop object
47566          * @param {String} targetId The target drag drop object
47567          * @param {event} e The raw browser event
47568          */
47569         "dragover" : true,
47570         /**
47571          * @event dragenter
47572          *  Fires when the dragged row(s) first cross another DD target while being dragged
47573          * @param {Grid} this
47574          * @param {Roo.GridDD} dd The drag drop object
47575          * @param {String} targetId The target drag drop object
47576          * @param {event} e The raw browser event
47577          */
47578         "dragenter" : true,
47579         /**
47580          * @event dragout
47581          * Fires when the dragged row(s) leave another DD target while being dragged
47582          * @param {Grid} this
47583          * @param {Roo.GridDD} dd The drag drop object
47584          * @param {String} targetId The target drag drop object
47585          * @param {event} e The raw browser event
47586          */
47587         "dragout" : true,
47588         /**
47589          * @event rowclass
47590          * Fires when a row is rendered, so you can change add a style to it.
47591          * @param {GridView} gridview   The grid view
47592          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47593          */
47594         'rowclass' : true,
47595
47596         /**
47597          * @event render
47598          * Fires when the grid is rendered
47599          * @param {Grid} grid
47600          */
47601         'render' : true
47602     });
47603
47604     Roo.grid.Grid.superclass.constructor.call(this);
47605 };
47606 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47607     
47608     /**
47609      * @cfg {String} ddGroup - drag drop group.
47610      */
47611
47612     /**
47613      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47614      */
47615     minColumnWidth : 25,
47616
47617     /**
47618      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47619      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47620      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47621      */
47622     autoSizeColumns : false,
47623
47624     /**
47625      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47626      */
47627     autoSizeHeaders : true,
47628
47629     /**
47630      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47631      */
47632     monitorWindowResize : true,
47633
47634     /**
47635      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47636      * rows measured to get a columns size. Default is 0 (all rows).
47637      */
47638     maxRowsToMeasure : 0,
47639
47640     /**
47641      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47642      */
47643     trackMouseOver : true,
47644
47645     /**
47646     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47647     */
47648     
47649     /**
47650     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47651     */
47652     enableDragDrop : false,
47653     
47654     /**
47655     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47656     */
47657     enableColumnMove : true,
47658     
47659     /**
47660     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47661     */
47662     enableColumnHide : true,
47663     
47664     /**
47665     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47666     */
47667     enableRowHeightSync : false,
47668     
47669     /**
47670     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47671     */
47672     stripeRows : true,
47673     
47674     /**
47675     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47676     */
47677     autoHeight : false,
47678
47679     /**
47680      * @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.
47681      */
47682     autoExpandColumn : false,
47683
47684     /**
47685     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47686     * Default is 50.
47687     */
47688     autoExpandMin : 50,
47689
47690     /**
47691     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47692     */
47693     autoExpandMax : 1000,
47694
47695     /**
47696     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47697     */
47698     view : null,
47699
47700     /**
47701     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
47702     */
47703     loadMask : false,
47704     /**
47705     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
47706     */
47707     dropTarget: false,
47708     
47709    
47710     
47711     // private
47712     rendered : false,
47713
47714     /**
47715     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
47716     * of a fixed width. Default is false.
47717     */
47718     /**
47719     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
47720     */
47721     /**
47722      * Called once after all setup has been completed and the grid is ready to be rendered.
47723      * @return {Roo.grid.Grid} this
47724      */
47725     render : function()
47726     {
47727         var c = this.container;
47728         // try to detect autoHeight/width mode
47729         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
47730             this.autoHeight = true;
47731         }
47732         var view = this.getView();
47733         view.init(this);
47734
47735         c.on("click", this.onClick, this);
47736         c.on("dblclick", this.onDblClick, this);
47737         c.on("contextmenu", this.onContextMenu, this);
47738         c.on("keydown", this.onKeyDown, this);
47739
47740         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
47741
47742         this.getSelectionModel().init(this);
47743
47744         view.render();
47745
47746         if(this.loadMask){
47747             this.loadMask = new Roo.LoadMask(this.container,
47748                     Roo.apply({store:this.dataSource}, this.loadMask));
47749         }
47750         
47751         
47752         if (this.toolbar && this.toolbar.xtype) {
47753             this.toolbar.container = this.getView().getHeaderPanel(true);
47754             this.toolbar = new Roo.Toolbar(this.toolbar);
47755         }
47756         if (this.footer && this.footer.xtype) {
47757             this.footer.dataSource = this.getDataSource();
47758             this.footer.container = this.getView().getFooterPanel(true);
47759             this.footer = Roo.factory(this.footer, Roo);
47760         }
47761         if (this.dropTarget && this.dropTarget.xtype) {
47762             delete this.dropTarget.xtype;
47763             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
47764         }
47765         
47766         
47767         this.rendered = true;
47768         this.fireEvent('render', this);
47769         return this;
47770     },
47771
47772         /**
47773          * Reconfigures the grid to use a different Store and Column Model.
47774          * The View will be bound to the new objects and refreshed.
47775          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
47776          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
47777          */
47778     reconfigure : function(dataSource, colModel){
47779         if(this.loadMask){
47780             this.loadMask.destroy();
47781             this.loadMask = new Roo.LoadMask(this.container,
47782                     Roo.apply({store:dataSource}, this.loadMask));
47783         }
47784         this.view.bind(dataSource, colModel);
47785         this.dataSource = dataSource;
47786         this.colModel = colModel;
47787         this.view.refresh(true);
47788     },
47789
47790     // private
47791     onKeyDown : function(e){
47792         this.fireEvent("keydown", e);
47793     },
47794
47795     /**
47796      * Destroy this grid.
47797      * @param {Boolean} removeEl True to remove the element
47798      */
47799     destroy : function(removeEl, keepListeners){
47800         if(this.loadMask){
47801             this.loadMask.destroy();
47802         }
47803         var c = this.container;
47804         c.removeAllListeners();
47805         this.view.destroy();
47806         this.colModel.purgeListeners();
47807         if(!keepListeners){
47808             this.purgeListeners();
47809         }
47810         c.update("");
47811         if(removeEl === true){
47812             c.remove();
47813         }
47814     },
47815
47816     // private
47817     processEvent : function(name, e){
47818         this.fireEvent(name, e);
47819         var t = e.getTarget();
47820         var v = this.view;
47821         var header = v.findHeaderIndex(t);
47822         if(header !== false){
47823             this.fireEvent("header" + name, this, header, e);
47824         }else{
47825             var row = v.findRowIndex(t);
47826             var cell = v.findCellIndex(t);
47827             if(row !== false){
47828                 this.fireEvent("row" + name, this, row, e);
47829                 if(cell !== false){
47830                     this.fireEvent("cell" + name, this, row, cell, e);
47831                 }
47832             }
47833         }
47834     },
47835
47836     // private
47837     onClick : function(e){
47838         this.processEvent("click", e);
47839     },
47840
47841     // private
47842     onContextMenu : function(e, t){
47843         this.processEvent("contextmenu", e);
47844     },
47845
47846     // private
47847     onDblClick : function(e){
47848         this.processEvent("dblclick", e);
47849     },
47850
47851     // private
47852     walkCells : function(row, col, step, fn, scope){
47853         var cm = this.colModel, clen = cm.getColumnCount();
47854         var ds = this.dataSource, rlen = ds.getCount(), first = true;
47855         if(step < 0){
47856             if(col < 0){
47857                 row--;
47858                 first = false;
47859             }
47860             while(row >= 0){
47861                 if(!first){
47862                     col = clen-1;
47863                 }
47864                 first = false;
47865                 while(col >= 0){
47866                     if(fn.call(scope || this, row, col, cm) === true){
47867                         return [row, col];
47868                     }
47869                     col--;
47870                 }
47871                 row--;
47872             }
47873         } else {
47874             if(col >= clen){
47875                 row++;
47876                 first = false;
47877             }
47878             while(row < rlen){
47879                 if(!first){
47880                     col = 0;
47881                 }
47882                 first = false;
47883                 while(col < clen){
47884                     if(fn.call(scope || this, row, col, cm) === true){
47885                         return [row, col];
47886                     }
47887                     col++;
47888                 }
47889                 row++;
47890             }
47891         }
47892         return null;
47893     },
47894
47895     // private
47896     getSelections : function(){
47897         return this.selModel.getSelections();
47898     },
47899
47900     /**
47901      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
47902      * but if manual update is required this method will initiate it.
47903      */
47904     autoSize : function(){
47905         if(this.rendered){
47906             this.view.layout();
47907             if(this.view.adjustForScroll){
47908                 this.view.adjustForScroll();
47909             }
47910         }
47911     },
47912
47913     /**
47914      * Returns the grid's underlying element.
47915      * @return {Element} The element
47916      */
47917     getGridEl : function(){
47918         return this.container;
47919     },
47920
47921     // private for compatibility, overridden by editor grid
47922     stopEditing : function(){},
47923
47924     /**
47925      * Returns the grid's SelectionModel.
47926      * @return {SelectionModel}
47927      */
47928     getSelectionModel : function(){
47929         if(!this.selModel){
47930             this.selModel = new Roo.grid.RowSelectionModel();
47931         }
47932         return this.selModel;
47933     },
47934
47935     /**
47936      * Returns the grid's DataSource.
47937      * @return {DataSource}
47938      */
47939     getDataSource : function(){
47940         return this.dataSource;
47941     },
47942
47943     /**
47944      * Returns the grid's ColumnModel.
47945      * @return {ColumnModel}
47946      */
47947     getColumnModel : function(){
47948         return this.colModel;
47949     },
47950
47951     /**
47952      * Returns the grid's GridView object.
47953      * @return {GridView}
47954      */
47955     getView : function(){
47956         if(!this.view){
47957             this.view = new Roo.grid.GridView(this.viewConfig);
47958         }
47959         return this.view;
47960     },
47961     /**
47962      * Called to get grid's drag proxy text, by default returns this.ddText.
47963      * @return {String}
47964      */
47965     getDragDropText : function(){
47966         var count = this.selModel.getCount();
47967         return String.format(this.ddText, count, count == 1 ? '' : 's');
47968     }
47969 });
47970 /**
47971  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
47972  * %0 is replaced with the number of selected rows.
47973  * @type String
47974  */
47975 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
47976  * Based on:
47977  * Ext JS Library 1.1.1
47978  * Copyright(c) 2006-2007, Ext JS, LLC.
47979  *
47980  * Originally Released Under LGPL - original licence link has changed is not relivant.
47981  *
47982  * Fork - LGPL
47983  * <script type="text/javascript">
47984  */
47985  
47986 Roo.grid.AbstractGridView = function(){
47987         this.grid = null;
47988         
47989         this.events = {
47990             "beforerowremoved" : true,
47991             "beforerowsinserted" : true,
47992             "beforerefresh" : true,
47993             "rowremoved" : true,
47994             "rowsinserted" : true,
47995             "rowupdated" : true,
47996             "refresh" : true
47997         };
47998     Roo.grid.AbstractGridView.superclass.constructor.call(this);
47999 };
48000
48001 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
48002     rowClass : "x-grid-row",
48003     cellClass : "x-grid-cell",
48004     tdClass : "x-grid-td",
48005     hdClass : "x-grid-hd",
48006     splitClass : "x-grid-hd-split",
48007     
48008         init: function(grid){
48009         this.grid = grid;
48010                 var cid = this.grid.getGridEl().id;
48011         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
48012         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
48013         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
48014         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
48015         },
48016         
48017         getColumnRenderers : function(){
48018         var renderers = [];
48019         var cm = this.grid.colModel;
48020         var colCount = cm.getColumnCount();
48021         for(var i = 0; i < colCount; i++){
48022             renderers[i] = cm.getRenderer(i);
48023         }
48024         return renderers;
48025     },
48026     
48027     getColumnIds : function(){
48028         var ids = [];
48029         var cm = this.grid.colModel;
48030         var colCount = cm.getColumnCount();
48031         for(var i = 0; i < colCount; i++){
48032             ids[i] = cm.getColumnId(i);
48033         }
48034         return ids;
48035     },
48036     
48037     getDataIndexes : function(){
48038         if(!this.indexMap){
48039             this.indexMap = this.buildIndexMap();
48040         }
48041         return this.indexMap.colToData;
48042     },
48043     
48044     getColumnIndexByDataIndex : function(dataIndex){
48045         if(!this.indexMap){
48046             this.indexMap = this.buildIndexMap();
48047         }
48048         return this.indexMap.dataToCol[dataIndex];
48049     },
48050     
48051     /**
48052      * Set a css style for a column dynamically. 
48053      * @param {Number} colIndex The index of the column
48054      * @param {String} name The css property name
48055      * @param {String} value The css value
48056      */
48057     setCSSStyle : function(colIndex, name, value){
48058         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
48059         Roo.util.CSS.updateRule(selector, name, value);
48060     },
48061     
48062     generateRules : function(cm){
48063         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48064         Roo.util.CSS.removeStyleSheet(rulesId);
48065         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48066             var cid = cm.getColumnId(i);
48067             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48068                          this.tdSelector, cid, " {\n}\n",
48069                          this.hdSelector, cid, " {\n}\n",
48070                          this.splitSelector, cid, " {\n}\n");
48071         }
48072         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48073     }
48074 });/*
48075  * Based on:
48076  * Ext JS Library 1.1.1
48077  * Copyright(c) 2006-2007, Ext JS, LLC.
48078  *
48079  * Originally Released Under LGPL - original licence link has changed is not relivant.
48080  *
48081  * Fork - LGPL
48082  * <script type="text/javascript">
48083  */
48084
48085 // private
48086 // This is a support class used internally by the Grid components
48087 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48088     this.grid = grid;
48089     this.view = grid.getView();
48090     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48091     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48092     if(hd2){
48093         this.setHandleElId(Roo.id(hd));
48094         this.setOuterHandleElId(Roo.id(hd2));
48095     }
48096     this.scroll = false;
48097 };
48098 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48099     maxDragWidth: 120,
48100     getDragData : function(e){
48101         var t = Roo.lib.Event.getTarget(e);
48102         var h = this.view.findHeaderCell(t);
48103         if(h){
48104             return {ddel: h.firstChild, header:h};
48105         }
48106         return false;
48107     },
48108
48109     onInitDrag : function(e){
48110         this.view.headersDisabled = true;
48111         var clone = this.dragData.ddel.cloneNode(true);
48112         clone.id = Roo.id();
48113         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48114         this.proxy.update(clone);
48115         return true;
48116     },
48117
48118     afterValidDrop : function(){
48119         var v = this.view;
48120         setTimeout(function(){
48121             v.headersDisabled = false;
48122         }, 50);
48123     },
48124
48125     afterInvalidDrop : function(){
48126         var v = this.view;
48127         setTimeout(function(){
48128             v.headersDisabled = false;
48129         }, 50);
48130     }
48131 });
48132 /*
48133  * Based on:
48134  * Ext JS Library 1.1.1
48135  * Copyright(c) 2006-2007, Ext JS, LLC.
48136  *
48137  * Originally Released Under LGPL - original licence link has changed is not relivant.
48138  *
48139  * Fork - LGPL
48140  * <script type="text/javascript">
48141  */
48142 // private
48143 // This is a support class used internally by the Grid components
48144 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48145     this.grid = grid;
48146     this.view = grid.getView();
48147     // split the proxies so they don't interfere with mouse events
48148     this.proxyTop = Roo.DomHelper.append(document.body, {
48149         cls:"col-move-top", html:"&#160;"
48150     }, true);
48151     this.proxyBottom = Roo.DomHelper.append(document.body, {
48152         cls:"col-move-bottom", html:"&#160;"
48153     }, true);
48154     this.proxyTop.hide = this.proxyBottom.hide = function(){
48155         this.setLeftTop(-100,-100);
48156         this.setStyle("visibility", "hidden");
48157     };
48158     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48159     // temporarily disabled
48160     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48161     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48162 };
48163 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48164     proxyOffsets : [-4, -9],
48165     fly: Roo.Element.fly,
48166
48167     getTargetFromEvent : function(e){
48168         var t = Roo.lib.Event.getTarget(e);
48169         var cindex = this.view.findCellIndex(t);
48170         if(cindex !== false){
48171             return this.view.getHeaderCell(cindex);
48172         }
48173         return null;
48174     },
48175
48176     nextVisible : function(h){
48177         var v = this.view, cm = this.grid.colModel;
48178         h = h.nextSibling;
48179         while(h){
48180             if(!cm.isHidden(v.getCellIndex(h))){
48181                 return h;
48182             }
48183             h = h.nextSibling;
48184         }
48185         return null;
48186     },
48187
48188     prevVisible : function(h){
48189         var v = this.view, cm = this.grid.colModel;
48190         h = h.prevSibling;
48191         while(h){
48192             if(!cm.isHidden(v.getCellIndex(h))){
48193                 return h;
48194             }
48195             h = h.prevSibling;
48196         }
48197         return null;
48198     },
48199
48200     positionIndicator : function(h, n, e){
48201         var x = Roo.lib.Event.getPageX(e);
48202         var r = Roo.lib.Dom.getRegion(n.firstChild);
48203         var px, pt, py = r.top + this.proxyOffsets[1];
48204         if((r.right - x) <= (r.right-r.left)/2){
48205             px = r.right+this.view.borderWidth;
48206             pt = "after";
48207         }else{
48208             px = r.left;
48209             pt = "before";
48210         }
48211         var oldIndex = this.view.getCellIndex(h);
48212         var newIndex = this.view.getCellIndex(n);
48213
48214         if(this.grid.colModel.isFixed(newIndex)){
48215             return false;
48216         }
48217
48218         var locked = this.grid.colModel.isLocked(newIndex);
48219
48220         if(pt == "after"){
48221             newIndex++;
48222         }
48223         if(oldIndex < newIndex){
48224             newIndex--;
48225         }
48226         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48227             return false;
48228         }
48229         px +=  this.proxyOffsets[0];
48230         this.proxyTop.setLeftTop(px, py);
48231         this.proxyTop.show();
48232         if(!this.bottomOffset){
48233             this.bottomOffset = this.view.mainHd.getHeight();
48234         }
48235         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48236         this.proxyBottom.show();
48237         return pt;
48238     },
48239
48240     onNodeEnter : function(n, dd, e, data){
48241         if(data.header != n){
48242             this.positionIndicator(data.header, n, e);
48243         }
48244     },
48245
48246     onNodeOver : function(n, dd, e, data){
48247         var result = false;
48248         if(data.header != n){
48249             result = this.positionIndicator(data.header, n, e);
48250         }
48251         if(!result){
48252             this.proxyTop.hide();
48253             this.proxyBottom.hide();
48254         }
48255         return result ? this.dropAllowed : this.dropNotAllowed;
48256     },
48257
48258     onNodeOut : function(n, dd, e, data){
48259         this.proxyTop.hide();
48260         this.proxyBottom.hide();
48261     },
48262
48263     onNodeDrop : function(n, dd, e, data){
48264         var h = data.header;
48265         if(h != n){
48266             var cm = this.grid.colModel;
48267             var x = Roo.lib.Event.getPageX(e);
48268             var r = Roo.lib.Dom.getRegion(n.firstChild);
48269             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
48270             var oldIndex = this.view.getCellIndex(h);
48271             var newIndex = this.view.getCellIndex(n);
48272             var locked = cm.isLocked(newIndex);
48273             if(pt == "after"){
48274                 newIndex++;
48275             }
48276             if(oldIndex < newIndex){
48277                 newIndex--;
48278             }
48279             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
48280                 return false;
48281             }
48282             cm.setLocked(oldIndex, locked, true);
48283             cm.moveColumn(oldIndex, newIndex);
48284             this.grid.fireEvent("columnmove", oldIndex, newIndex);
48285             return true;
48286         }
48287         return false;
48288     }
48289 });
48290 /*
48291  * Based on:
48292  * Ext JS Library 1.1.1
48293  * Copyright(c) 2006-2007, Ext JS, LLC.
48294  *
48295  * Originally Released Under LGPL - original licence link has changed is not relivant.
48296  *
48297  * Fork - LGPL
48298  * <script type="text/javascript">
48299  */
48300   
48301 /**
48302  * @class Roo.grid.GridView
48303  * @extends Roo.util.Observable
48304  *
48305  * @constructor
48306  * @param {Object} config
48307  */
48308 Roo.grid.GridView = function(config){
48309     Roo.grid.GridView.superclass.constructor.call(this);
48310     this.el = null;
48311
48312     Roo.apply(this, config);
48313 };
48314
48315 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
48316
48317     /**
48318      * Override this function to apply custom css classes to rows during rendering
48319      * @param {Record} record The record
48320      * @param {Number} index
48321      * @method getRowClass
48322      */
48323     rowClass : "x-grid-row",
48324
48325     cellClass : "x-grid-col",
48326
48327     tdClass : "x-grid-td",
48328
48329     hdClass : "x-grid-hd",
48330
48331     splitClass : "x-grid-split",
48332
48333     sortClasses : ["sort-asc", "sort-desc"],
48334
48335     enableMoveAnim : false,
48336
48337     hlColor: "C3DAF9",
48338
48339     dh : Roo.DomHelper,
48340
48341     fly : Roo.Element.fly,
48342
48343     css : Roo.util.CSS,
48344
48345     borderWidth: 1,
48346
48347     splitOffset: 3,
48348
48349     scrollIncrement : 22,
48350
48351     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
48352
48353     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
48354
48355     bind : function(ds, cm){
48356         if(this.ds){
48357             this.ds.un("load", this.onLoad, this);
48358             this.ds.un("datachanged", this.onDataChange, this);
48359             this.ds.un("add", this.onAdd, this);
48360             this.ds.un("remove", this.onRemove, this);
48361             this.ds.un("update", this.onUpdate, this);
48362             this.ds.un("clear", this.onClear, this);
48363         }
48364         if(ds){
48365             ds.on("load", this.onLoad, this);
48366             ds.on("datachanged", this.onDataChange, this);
48367             ds.on("add", this.onAdd, this);
48368             ds.on("remove", this.onRemove, this);
48369             ds.on("update", this.onUpdate, this);
48370             ds.on("clear", this.onClear, this);
48371         }
48372         this.ds = ds;
48373
48374         if(this.cm){
48375             this.cm.un("widthchange", this.onColWidthChange, this);
48376             this.cm.un("headerchange", this.onHeaderChange, this);
48377             this.cm.un("hiddenchange", this.onHiddenChange, this);
48378             this.cm.un("columnmoved", this.onColumnMove, this);
48379             this.cm.un("columnlockchange", this.onColumnLock, this);
48380         }
48381         if(cm){
48382             this.generateRules(cm);
48383             cm.on("widthchange", this.onColWidthChange, this);
48384             cm.on("headerchange", this.onHeaderChange, this);
48385             cm.on("hiddenchange", this.onHiddenChange, this);
48386             cm.on("columnmoved", this.onColumnMove, this);
48387             cm.on("columnlockchange", this.onColumnLock, this);
48388         }
48389         this.cm = cm;
48390     },
48391
48392     init: function(grid){
48393         Roo.grid.GridView.superclass.init.call(this, grid);
48394
48395         this.bind(grid.dataSource, grid.colModel);
48396
48397         grid.on("headerclick", this.handleHeaderClick, this);
48398
48399         if(grid.trackMouseOver){
48400             grid.on("mouseover", this.onRowOver, this);
48401             grid.on("mouseout", this.onRowOut, this);
48402         }
48403         grid.cancelTextSelection = function(){};
48404         this.gridId = grid.id;
48405
48406         var tpls = this.templates || {};
48407
48408         if(!tpls.master){
48409             tpls.master = new Roo.Template(
48410                '<div class="x-grid" hidefocus="true">',
48411                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48412                   '<div class="x-grid-topbar"></div>',
48413                   '<div class="x-grid-scroller"><div></div></div>',
48414                   '<div class="x-grid-locked">',
48415                       '<div class="x-grid-header">{lockedHeader}</div>',
48416                       '<div class="x-grid-body">{lockedBody}</div>',
48417                   "</div>",
48418                   '<div class="x-grid-viewport">',
48419                       '<div class="x-grid-header">{header}</div>',
48420                       '<div class="x-grid-body">{body}</div>',
48421                   "</div>",
48422                   '<div class="x-grid-bottombar"></div>',
48423                  
48424                   '<div class="x-grid-resize-proxy">&#160;</div>',
48425                "</div>"
48426             );
48427             tpls.master.disableformats = true;
48428         }
48429
48430         if(!tpls.header){
48431             tpls.header = new Roo.Template(
48432                '<table border="0" cellspacing="0" cellpadding="0">',
48433                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48434                "</table>{splits}"
48435             );
48436             tpls.header.disableformats = true;
48437         }
48438         tpls.header.compile();
48439
48440         if(!tpls.hcell){
48441             tpls.hcell = new Roo.Template(
48442                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48443                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48444                 "</div></td>"
48445              );
48446              tpls.hcell.disableFormats = true;
48447         }
48448         tpls.hcell.compile();
48449
48450         if(!tpls.hsplit){
48451             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48452             tpls.hsplit.disableFormats = true;
48453         }
48454         tpls.hsplit.compile();
48455
48456         if(!tpls.body){
48457             tpls.body = new Roo.Template(
48458                '<table border="0" cellspacing="0" cellpadding="0">',
48459                "<tbody>{rows}</tbody>",
48460                "</table>"
48461             );
48462             tpls.body.disableFormats = true;
48463         }
48464         tpls.body.compile();
48465
48466         if(!tpls.row){
48467             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48468             tpls.row.disableFormats = true;
48469         }
48470         tpls.row.compile();
48471
48472         if(!tpls.cell){
48473             tpls.cell = new Roo.Template(
48474                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48475                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48476                 "</td>"
48477             );
48478             tpls.cell.disableFormats = true;
48479         }
48480         tpls.cell.compile();
48481
48482         this.templates = tpls;
48483     },
48484
48485     // remap these for backwards compat
48486     onColWidthChange : function(){
48487         this.updateColumns.apply(this, arguments);
48488     },
48489     onHeaderChange : function(){
48490         this.updateHeaders.apply(this, arguments);
48491     }, 
48492     onHiddenChange : function(){
48493         this.handleHiddenChange.apply(this, arguments);
48494     },
48495     onColumnMove : function(){
48496         this.handleColumnMove.apply(this, arguments);
48497     },
48498     onColumnLock : function(){
48499         this.handleLockChange.apply(this, arguments);
48500     },
48501
48502     onDataChange : function(){
48503         this.refresh();
48504         this.updateHeaderSortState();
48505     },
48506
48507     onClear : function(){
48508         this.refresh();
48509     },
48510
48511     onUpdate : function(ds, record){
48512         this.refreshRow(record);
48513     },
48514
48515     refreshRow : function(record){
48516         var ds = this.ds, index;
48517         if(typeof record == 'number'){
48518             index = record;
48519             record = ds.getAt(index);
48520         }else{
48521             index = ds.indexOf(record);
48522         }
48523         this.insertRows(ds, index, index, true);
48524         this.onRemove(ds, record, index+1, true);
48525         this.syncRowHeights(index, index);
48526         this.layout();
48527         this.fireEvent("rowupdated", this, index, record);
48528     },
48529
48530     onAdd : function(ds, records, index){
48531         this.insertRows(ds, index, index + (records.length-1));
48532     },
48533
48534     onRemove : function(ds, record, index, isUpdate){
48535         if(isUpdate !== true){
48536             this.fireEvent("beforerowremoved", this, index, record);
48537         }
48538         var bt = this.getBodyTable(), lt = this.getLockedTable();
48539         if(bt.rows[index]){
48540             bt.firstChild.removeChild(bt.rows[index]);
48541         }
48542         if(lt.rows[index]){
48543             lt.firstChild.removeChild(lt.rows[index]);
48544         }
48545         if(isUpdate !== true){
48546             this.stripeRows(index);
48547             this.syncRowHeights(index, index);
48548             this.layout();
48549             this.fireEvent("rowremoved", this, index, record);
48550         }
48551     },
48552
48553     onLoad : function(){
48554         this.scrollToTop();
48555     },
48556
48557     /**
48558      * Scrolls the grid to the top
48559      */
48560     scrollToTop : function(){
48561         if(this.scroller){
48562             this.scroller.dom.scrollTop = 0;
48563             this.syncScroll();
48564         }
48565     },
48566
48567     /**
48568      * Gets a panel in the header of the grid that can be used for toolbars etc.
48569      * After modifying the contents of this panel a call to grid.autoSize() may be
48570      * required to register any changes in size.
48571      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48572      * @return Roo.Element
48573      */
48574     getHeaderPanel : function(doShow){
48575         if(doShow){
48576             this.headerPanel.show();
48577         }
48578         return this.headerPanel;
48579     },
48580
48581     /**
48582      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48583      * After modifying the contents of this panel a call to grid.autoSize() may be
48584      * required to register any changes in size.
48585      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48586      * @return Roo.Element
48587      */
48588     getFooterPanel : function(doShow){
48589         if(doShow){
48590             this.footerPanel.show();
48591         }
48592         return this.footerPanel;
48593     },
48594
48595     initElements : function(){
48596         var E = Roo.Element;
48597         var el = this.grid.getGridEl().dom.firstChild;
48598         var cs = el.childNodes;
48599
48600         this.el = new E(el);
48601         
48602          this.focusEl = new E(el.firstChild);
48603         this.focusEl.swallowEvent("click", true);
48604         
48605         this.headerPanel = new E(cs[1]);
48606         this.headerPanel.enableDisplayMode("block");
48607
48608         this.scroller = new E(cs[2]);
48609         this.scrollSizer = new E(this.scroller.dom.firstChild);
48610
48611         this.lockedWrap = new E(cs[3]);
48612         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48613         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48614
48615         this.mainWrap = new E(cs[4]);
48616         this.mainHd = new E(this.mainWrap.dom.firstChild);
48617         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48618
48619         this.footerPanel = new E(cs[5]);
48620         this.footerPanel.enableDisplayMode("block");
48621
48622         this.resizeProxy = new E(cs[6]);
48623
48624         this.headerSelector = String.format(
48625            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48626            this.lockedHd.id, this.mainHd.id
48627         );
48628
48629         this.splitterSelector = String.format(
48630            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48631            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48632         );
48633     },
48634     idToCssName : function(s)
48635     {
48636         return s.replace(/[^a-z0-9]+/ig, '-');
48637     },
48638
48639     getHeaderCell : function(index){
48640         return Roo.DomQuery.select(this.headerSelector)[index];
48641     },
48642
48643     getHeaderCellMeasure : function(index){
48644         return this.getHeaderCell(index).firstChild;
48645     },
48646
48647     getHeaderCellText : function(index){
48648         return this.getHeaderCell(index).firstChild.firstChild;
48649     },
48650
48651     getLockedTable : function(){
48652         return this.lockedBody.dom.firstChild;
48653     },
48654
48655     getBodyTable : function(){
48656         return this.mainBody.dom.firstChild;
48657     },
48658
48659     getLockedRow : function(index){
48660         return this.getLockedTable().rows[index];
48661     },
48662
48663     getRow : function(index){
48664         return this.getBodyTable().rows[index];
48665     },
48666
48667     getRowComposite : function(index){
48668         if(!this.rowEl){
48669             this.rowEl = new Roo.CompositeElementLite();
48670         }
48671         var els = [], lrow, mrow;
48672         if(lrow = this.getLockedRow(index)){
48673             els.push(lrow);
48674         }
48675         if(mrow = this.getRow(index)){
48676             els.push(mrow);
48677         }
48678         this.rowEl.elements = els;
48679         return this.rowEl;
48680     },
48681     /**
48682      * Gets the 'td' of the cell
48683      * 
48684      * @param {Integer} rowIndex row to select
48685      * @param {Integer} colIndex column to select
48686      * 
48687      * @return {Object} 
48688      */
48689     getCell : function(rowIndex, colIndex){
48690         var locked = this.cm.getLockedCount();
48691         var source;
48692         if(colIndex < locked){
48693             source = this.lockedBody.dom.firstChild;
48694         }else{
48695             source = this.mainBody.dom.firstChild;
48696             colIndex -= locked;
48697         }
48698         return source.rows[rowIndex].childNodes[colIndex];
48699     },
48700
48701     getCellText : function(rowIndex, colIndex){
48702         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48703     },
48704
48705     getCellBox : function(cell){
48706         var b = this.fly(cell).getBox();
48707         if(Roo.isOpera){ // opera fails to report the Y
48708             b.y = cell.offsetTop + this.mainBody.getY();
48709         }
48710         return b;
48711     },
48712
48713     getCellIndex : function(cell){
48714         var id = String(cell.className).match(this.cellRE);
48715         if(id){
48716             return parseInt(id[1], 10);
48717         }
48718         return 0;
48719     },
48720
48721     findHeaderIndex : function(n){
48722         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48723         return r ? this.getCellIndex(r) : false;
48724     },
48725
48726     findHeaderCell : function(n){
48727         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48728         return r ? r : false;
48729     },
48730
48731     findRowIndex : function(n){
48732         if(!n){
48733             return false;
48734         }
48735         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
48736         return r ? r.rowIndex : false;
48737     },
48738
48739     findCellIndex : function(node){
48740         var stop = this.el.dom;
48741         while(node && node != stop){
48742             if(this.findRE.test(node.className)){
48743                 return this.getCellIndex(node);
48744             }
48745             node = node.parentNode;
48746         }
48747         return false;
48748     },
48749
48750     getColumnId : function(index){
48751         return this.cm.getColumnId(index);
48752     },
48753
48754     getSplitters : function()
48755     {
48756         if(this.splitterSelector){
48757            return Roo.DomQuery.select(this.splitterSelector);
48758         }else{
48759             return null;
48760       }
48761     },
48762
48763     getSplitter : function(index){
48764         return this.getSplitters()[index];
48765     },
48766
48767     onRowOver : function(e, t){
48768         var row;
48769         if((row = this.findRowIndex(t)) !== false){
48770             this.getRowComposite(row).addClass("x-grid-row-over");
48771         }
48772     },
48773
48774     onRowOut : function(e, t){
48775         var row;
48776         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
48777             this.getRowComposite(row).removeClass("x-grid-row-over");
48778         }
48779     },
48780
48781     renderHeaders : function(){
48782         var cm = this.cm;
48783         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
48784         var cb = [], lb = [], sb = [], lsb = [], p = {};
48785         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48786             p.cellId = "x-grid-hd-0-" + i;
48787             p.splitId = "x-grid-csplit-0-" + i;
48788             p.id = cm.getColumnId(i);
48789             p.title = cm.getColumnTooltip(i) || "";
48790             p.value = cm.getColumnHeader(i) || "";
48791             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
48792             if(!cm.isLocked(i)){
48793                 cb[cb.length] = ct.apply(p);
48794                 sb[sb.length] = st.apply(p);
48795             }else{
48796                 lb[lb.length] = ct.apply(p);
48797                 lsb[lsb.length] = st.apply(p);
48798             }
48799         }
48800         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
48801                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
48802     },
48803
48804     updateHeaders : function(){
48805         var html = this.renderHeaders();
48806         this.lockedHd.update(html[0]);
48807         this.mainHd.update(html[1]);
48808     },
48809
48810     /**
48811      * Focuses the specified row.
48812      * @param {Number} row The row index
48813      */
48814     focusRow : function(row)
48815     {
48816         //Roo.log('GridView.focusRow');
48817         var x = this.scroller.dom.scrollLeft;
48818         this.focusCell(row, 0, false);
48819         this.scroller.dom.scrollLeft = x;
48820     },
48821
48822     /**
48823      * Focuses the specified cell.
48824      * @param {Number} row The row index
48825      * @param {Number} col The column index
48826      * @param {Boolean} hscroll false to disable horizontal scrolling
48827      */
48828     focusCell : function(row, col, hscroll)
48829     {
48830         //Roo.log('GridView.focusCell');
48831         var el = this.ensureVisible(row, col, hscroll);
48832         this.focusEl.alignTo(el, "tl-tl");
48833         if(Roo.isGecko){
48834             this.focusEl.focus();
48835         }else{
48836             this.focusEl.focus.defer(1, this.focusEl);
48837         }
48838     },
48839
48840     /**
48841      * Scrolls the specified cell into view
48842      * @param {Number} row The row index
48843      * @param {Number} col The column index
48844      * @param {Boolean} hscroll false to disable horizontal scrolling
48845      */
48846     ensureVisible : function(row, col, hscroll)
48847     {
48848         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
48849         //return null; //disable for testing.
48850         if(typeof row != "number"){
48851             row = row.rowIndex;
48852         }
48853         if(row < 0 && row >= this.ds.getCount()){
48854             return  null;
48855         }
48856         col = (col !== undefined ? col : 0);
48857         var cm = this.grid.colModel;
48858         while(cm.isHidden(col)){
48859             col++;
48860         }
48861
48862         var el = this.getCell(row, col);
48863         if(!el){
48864             return null;
48865         }
48866         var c = this.scroller.dom;
48867
48868         var ctop = parseInt(el.offsetTop, 10);
48869         var cleft = parseInt(el.offsetLeft, 10);
48870         var cbot = ctop + el.offsetHeight;
48871         var cright = cleft + el.offsetWidth;
48872         
48873         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
48874         var stop = parseInt(c.scrollTop, 10);
48875         var sleft = parseInt(c.scrollLeft, 10);
48876         var sbot = stop + ch;
48877         var sright = sleft + c.clientWidth;
48878         /*
48879         Roo.log('GridView.ensureVisible:' +
48880                 ' ctop:' + ctop +
48881                 ' c.clientHeight:' + c.clientHeight +
48882                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
48883                 ' stop:' + stop +
48884                 ' cbot:' + cbot +
48885                 ' sbot:' + sbot +
48886                 ' ch:' + ch  
48887                 );
48888         */
48889         if(ctop < stop){
48890              c.scrollTop = ctop;
48891             //Roo.log("set scrolltop to ctop DISABLE?");
48892         }else if(cbot > sbot){
48893             //Roo.log("set scrolltop to cbot-ch");
48894             c.scrollTop = cbot-ch;
48895         }
48896         
48897         if(hscroll !== false){
48898             if(cleft < sleft){
48899                 c.scrollLeft = cleft;
48900             }else if(cright > sright){
48901                 c.scrollLeft = cright-c.clientWidth;
48902             }
48903         }
48904          
48905         return el;
48906     },
48907
48908     updateColumns : function(){
48909         this.grid.stopEditing();
48910         var cm = this.grid.colModel, colIds = this.getColumnIds();
48911         //var totalWidth = cm.getTotalWidth();
48912         var pos = 0;
48913         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48914             //if(cm.isHidden(i)) continue;
48915             var w = cm.getColumnWidth(i);
48916             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48917             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48918         }
48919         this.updateSplitters();
48920     },
48921
48922     generateRules : function(cm){
48923         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
48924         Roo.util.CSS.removeStyleSheet(rulesId);
48925         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48926             var cid = cm.getColumnId(i);
48927             var align = '';
48928             if(cm.config[i].align){
48929                 align = 'text-align:'+cm.config[i].align+';';
48930             }
48931             var hidden = '';
48932             if(cm.isHidden(i)){
48933                 hidden = 'display:none;';
48934             }
48935             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
48936             ruleBuf.push(
48937                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
48938                     this.hdSelector, cid, " {\n", align, width, "}\n",
48939                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
48940                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
48941         }
48942         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48943     },
48944
48945     updateSplitters : function(){
48946         var cm = this.cm, s = this.getSplitters();
48947         if(s){ // splitters not created yet
48948             var pos = 0, locked = true;
48949             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48950                 if(cm.isHidden(i)) continue;
48951                 var w = cm.getColumnWidth(i); // make sure it's a number
48952                 if(!cm.isLocked(i) && locked){
48953                     pos = 0;
48954                     locked = false;
48955                 }
48956                 pos += w;
48957                 s[i].style.left = (pos-this.splitOffset) + "px";
48958             }
48959         }
48960     },
48961
48962     handleHiddenChange : function(colModel, colIndex, hidden){
48963         if(hidden){
48964             this.hideColumn(colIndex);
48965         }else{
48966             this.unhideColumn(colIndex);
48967         }
48968     },
48969
48970     hideColumn : function(colIndex){
48971         var cid = this.getColumnId(colIndex);
48972         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
48973         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
48974         if(Roo.isSafari){
48975             this.updateHeaders();
48976         }
48977         this.updateSplitters();
48978         this.layout();
48979     },
48980
48981     unhideColumn : function(colIndex){
48982         var cid = this.getColumnId(colIndex);
48983         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
48984         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
48985
48986         if(Roo.isSafari){
48987             this.updateHeaders();
48988         }
48989         this.updateSplitters();
48990         this.layout();
48991     },
48992
48993     insertRows : function(dm, firstRow, lastRow, isUpdate){
48994         if(firstRow == 0 && lastRow == dm.getCount()-1){
48995             this.refresh();
48996         }else{
48997             if(!isUpdate){
48998                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
48999             }
49000             var s = this.getScrollState();
49001             var markup = this.renderRows(firstRow, lastRow);
49002             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
49003             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
49004             this.restoreScroll(s);
49005             if(!isUpdate){
49006                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
49007                 this.syncRowHeights(firstRow, lastRow);
49008                 this.stripeRows(firstRow);
49009                 this.layout();
49010             }
49011         }
49012     },
49013
49014     bufferRows : function(markup, target, index){
49015         var before = null, trows = target.rows, tbody = target.tBodies[0];
49016         if(index < trows.length){
49017             before = trows[index];
49018         }
49019         var b = document.createElement("div");
49020         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
49021         var rows = b.firstChild.rows;
49022         for(var i = 0, len = rows.length; i < len; i++){
49023             if(before){
49024                 tbody.insertBefore(rows[0], before);
49025             }else{
49026                 tbody.appendChild(rows[0]);
49027             }
49028         }
49029         b.innerHTML = "";
49030         b = null;
49031     },
49032
49033     deleteRows : function(dm, firstRow, lastRow){
49034         if(dm.getRowCount()<1){
49035             this.fireEvent("beforerefresh", this);
49036             this.mainBody.update("");
49037             this.lockedBody.update("");
49038             this.fireEvent("refresh", this);
49039         }else{
49040             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
49041             var bt = this.getBodyTable();
49042             var tbody = bt.firstChild;
49043             var rows = bt.rows;
49044             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
49045                 tbody.removeChild(rows[firstRow]);
49046             }
49047             this.stripeRows(firstRow);
49048             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
49049         }
49050     },
49051
49052     updateRows : function(dataSource, firstRow, lastRow){
49053         var s = this.getScrollState();
49054         this.refresh();
49055         this.restoreScroll(s);
49056     },
49057
49058     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
49059         if(!noRefresh){
49060            this.refresh();
49061         }
49062         this.updateHeaderSortState();
49063     },
49064
49065     getScrollState : function(){
49066         
49067         var sb = this.scroller.dom;
49068         return {left: sb.scrollLeft, top: sb.scrollTop};
49069     },
49070
49071     stripeRows : function(startRow){
49072         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49073             return;
49074         }
49075         startRow = startRow || 0;
49076         var rows = this.getBodyTable().rows;
49077         var lrows = this.getLockedTable().rows;
49078         var cls = ' x-grid-row-alt ';
49079         for(var i = startRow, len = rows.length; i < len; i++){
49080             var row = rows[i], lrow = lrows[i];
49081             var isAlt = ((i+1) % 2 == 0);
49082             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49083             if(isAlt == hasAlt){
49084                 continue;
49085             }
49086             if(isAlt){
49087                 row.className += " x-grid-row-alt";
49088             }else{
49089                 row.className = row.className.replace("x-grid-row-alt", "");
49090             }
49091             if(lrow){
49092                 lrow.className = row.className;
49093             }
49094         }
49095     },
49096
49097     restoreScroll : function(state){
49098         //Roo.log('GridView.restoreScroll');
49099         var sb = this.scroller.dom;
49100         sb.scrollLeft = state.left;
49101         sb.scrollTop = state.top;
49102         this.syncScroll();
49103     },
49104
49105     syncScroll : function(){
49106         //Roo.log('GridView.syncScroll');
49107         var sb = this.scroller.dom;
49108         var sh = this.mainHd.dom;
49109         var bs = this.mainBody.dom;
49110         var lv = this.lockedBody.dom;
49111         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49112         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49113     },
49114
49115     handleScroll : function(e){
49116         this.syncScroll();
49117         var sb = this.scroller.dom;
49118         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49119         e.stopEvent();
49120     },
49121
49122     handleWheel : function(e){
49123         var d = e.getWheelDelta();
49124         this.scroller.dom.scrollTop -= d*22;
49125         // set this here to prevent jumpy scrolling on large tables
49126         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49127         e.stopEvent();
49128     },
49129
49130     renderRows : function(startRow, endRow){
49131         // pull in all the crap needed to render rows
49132         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49133         var colCount = cm.getColumnCount();
49134
49135         if(ds.getCount() < 1){
49136             return ["", ""];
49137         }
49138
49139         // build a map for all the columns
49140         var cs = [];
49141         for(var i = 0; i < colCount; i++){
49142             var name = cm.getDataIndex(i);
49143             cs[i] = {
49144                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49145                 renderer : cm.getRenderer(i),
49146                 id : cm.getColumnId(i),
49147                 locked : cm.isLocked(i)
49148             };
49149         }
49150
49151         startRow = startRow || 0;
49152         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49153
49154         // records to render
49155         var rs = ds.getRange(startRow, endRow);
49156
49157         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49158     },
49159
49160     // As much as I hate to duplicate code, this was branched because FireFox really hates
49161     // [].join("") on strings. The performance difference was substantial enough to
49162     // branch this function
49163     doRender : Roo.isGecko ?
49164             function(cs, rs, ds, startRow, colCount, stripe){
49165                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49166                 // buffers
49167                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49168                 
49169                 var hasListener = this.grid.hasListener('rowclass');
49170                 var rowcfg = {};
49171                 for(var j = 0, len = rs.length; j < len; j++){
49172                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49173                     for(var i = 0; i < colCount; i++){
49174                         c = cs[i];
49175                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49176                         p.id = c.id;
49177                         p.css = p.attr = "";
49178                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49179                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49180                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49181                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49182                         }
49183                         var markup = ct.apply(p);
49184                         if(!c.locked){
49185                             cb+= markup;
49186                         }else{
49187                             lcb+= markup;
49188                         }
49189                     }
49190                     var alt = [];
49191                     if(stripe && ((rowIndex+1) % 2 == 0)){
49192                         alt.push("x-grid-row-alt")
49193                     }
49194                     if(r.dirty){
49195                         alt.push(  " x-grid-dirty-row");
49196                     }
49197                     rp.cells = lcb;
49198                     if(this.getRowClass){
49199                         alt.push(this.getRowClass(r, rowIndex));
49200                     }
49201                     if (hasListener) {
49202                         rowcfg = {
49203                              
49204                             record: r,
49205                             rowIndex : rowIndex,
49206                             rowClass : ''
49207                         }
49208                         this.grid.fireEvent('rowclass', this, rowcfg);
49209                         alt.push(rowcfg.rowClass);
49210                     }
49211                     rp.alt = alt.join(" ");
49212                     lbuf+= rt.apply(rp);
49213                     rp.cells = cb;
49214                     buf+=  rt.apply(rp);
49215                 }
49216                 return [lbuf, buf];
49217             } :
49218             function(cs, rs, ds, startRow, colCount, stripe){
49219                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49220                 // buffers
49221                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49222                 var hasListener = this.grid.hasListener('rowclass');
49223                 var rowcfg = {};
49224                 for(var j = 0, len = rs.length; j < len; j++){
49225                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49226                     for(var i = 0; i < colCount; i++){
49227                         c = cs[i];
49228                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49229                         p.id = c.id;
49230                         p.css = p.attr = "";
49231                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49232                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49233                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49234                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49235                         }
49236                         var markup = ct.apply(p);
49237                         if(!c.locked){
49238                             cb[cb.length] = markup;
49239                         }else{
49240                             lcb[lcb.length] = markup;
49241                         }
49242                     }
49243                     var alt = [];
49244                     if(stripe && ((rowIndex+1) % 2 == 0)){
49245                         alt.push( "x-grid-row-alt");
49246                     }
49247                     if(r.dirty){
49248                         alt.push(" x-grid-dirty-row");
49249                     }
49250                     rp.cells = lcb;
49251                     if(this.getRowClass){
49252                         alt.push( this.getRowClass(r, rowIndex));
49253                     }
49254                     if (hasListener) {
49255                         rowcfg = {
49256                              
49257                             record: r,
49258                             rowIndex : rowIndex,
49259                             rowClass : ''
49260                         }
49261                         this.grid.fireEvent('rowclass', this, rowcfg);
49262                         alt.push(rowcfg.rowClass);
49263                     }
49264                     rp.alt = alt.join(" ");
49265                     rp.cells = lcb.join("");
49266                     lbuf[lbuf.length] = rt.apply(rp);
49267                     rp.cells = cb.join("");
49268                     buf[buf.length] =  rt.apply(rp);
49269                 }
49270                 return [lbuf.join(""), buf.join("")];
49271             },
49272
49273     renderBody : function(){
49274         var markup = this.renderRows();
49275         var bt = this.templates.body;
49276         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
49277     },
49278
49279     /**
49280      * Refreshes the grid
49281      * @param {Boolean} headersToo
49282      */
49283     refresh : function(headersToo){
49284         this.fireEvent("beforerefresh", this);
49285         this.grid.stopEditing();
49286         var result = this.renderBody();
49287         this.lockedBody.update(result[0]);
49288         this.mainBody.update(result[1]);
49289         if(headersToo === true){
49290             this.updateHeaders();
49291             this.updateColumns();
49292             this.updateSplitters();
49293             this.updateHeaderSortState();
49294         }
49295         this.syncRowHeights();
49296         this.layout();
49297         this.fireEvent("refresh", this);
49298     },
49299
49300     handleColumnMove : function(cm, oldIndex, newIndex){
49301         this.indexMap = null;
49302         var s = this.getScrollState();
49303         this.refresh(true);
49304         this.restoreScroll(s);
49305         this.afterMove(newIndex);
49306     },
49307
49308     afterMove : function(colIndex){
49309         if(this.enableMoveAnim && Roo.enableFx){
49310             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
49311         }
49312         // if multisort - fix sortOrder, and reload..
49313         if (this.grid.dataSource.multiSort) {
49314             // the we can call sort again..
49315             var dm = this.grid.dataSource;
49316             var cm = this.grid.colModel;
49317             var so = [];
49318             for(var i = 0; i < cm.config.length; i++ ) {
49319                 
49320                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
49321                     continue; // dont' bother, it's not in sort list or being set.
49322                 }
49323                 
49324                 so.push(cm.config[i].dataIndex);
49325             };
49326             dm.sortOrder = so;
49327             dm.load(dm.lastOptions);
49328             
49329             
49330         }
49331         
49332     },
49333
49334     updateCell : function(dm, rowIndex, dataIndex){
49335         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
49336         if(typeof colIndex == "undefined"){ // not present in grid
49337             return;
49338         }
49339         var cm = this.grid.colModel;
49340         var cell = this.getCell(rowIndex, colIndex);
49341         var cellText = this.getCellText(rowIndex, colIndex);
49342
49343         var p = {
49344             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
49345             id : cm.getColumnId(colIndex),
49346             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
49347         };
49348         var renderer = cm.getRenderer(colIndex);
49349         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
49350         if(typeof val == "undefined" || val === "") val = "&#160;";
49351         cellText.innerHTML = val;
49352         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
49353         this.syncRowHeights(rowIndex, rowIndex);
49354     },
49355
49356     calcColumnWidth : function(colIndex, maxRowsToMeasure){
49357         var maxWidth = 0;
49358         if(this.grid.autoSizeHeaders){
49359             var h = this.getHeaderCellMeasure(colIndex);
49360             maxWidth = Math.max(maxWidth, h.scrollWidth);
49361         }
49362         var tb, index;
49363         if(this.cm.isLocked(colIndex)){
49364             tb = this.getLockedTable();
49365             index = colIndex;
49366         }else{
49367             tb = this.getBodyTable();
49368             index = colIndex - this.cm.getLockedCount();
49369         }
49370         if(tb && tb.rows){
49371             var rows = tb.rows;
49372             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
49373             for(var i = 0; i < stopIndex; i++){
49374                 var cell = rows[i].childNodes[index].firstChild;
49375                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
49376             }
49377         }
49378         return maxWidth + /*margin for error in IE*/ 5;
49379     },
49380     /**
49381      * Autofit a column to its content.
49382      * @param {Number} colIndex
49383      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
49384      */
49385      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
49386          if(this.cm.isHidden(colIndex)){
49387              return; // can't calc a hidden column
49388          }
49389         if(forceMinSize){
49390             var cid = this.cm.getColumnId(colIndex);
49391             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
49392            if(this.grid.autoSizeHeaders){
49393                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
49394            }
49395         }
49396         var newWidth = this.calcColumnWidth(colIndex);
49397         this.cm.setColumnWidth(colIndex,
49398             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49399         if(!suppressEvent){
49400             this.grid.fireEvent("columnresize", colIndex, newWidth);
49401         }
49402     },
49403
49404     /**
49405      * Autofits all columns to their content and then expands to fit any extra space in the grid
49406      */
49407      autoSizeColumns : function(){
49408         var cm = this.grid.colModel;
49409         var colCount = cm.getColumnCount();
49410         for(var i = 0; i < colCount; i++){
49411             this.autoSizeColumn(i, true, true);
49412         }
49413         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49414             this.fitColumns();
49415         }else{
49416             this.updateColumns();
49417             this.layout();
49418         }
49419     },
49420
49421     /**
49422      * Autofits all columns to the grid's width proportionate with their current size
49423      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49424      */
49425     fitColumns : function(reserveScrollSpace){
49426         var cm = this.grid.colModel;
49427         var colCount = cm.getColumnCount();
49428         var cols = [];
49429         var width = 0;
49430         var i, w;
49431         for (i = 0; i < colCount; i++){
49432             if(!cm.isHidden(i) && !cm.isFixed(i)){
49433                 w = cm.getColumnWidth(i);
49434                 cols.push(i);
49435                 cols.push(w);
49436                 width += w;
49437             }
49438         }
49439         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49440         if(reserveScrollSpace){
49441             avail -= 17;
49442         }
49443         var frac = (avail - cm.getTotalWidth())/width;
49444         while (cols.length){
49445             w = cols.pop();
49446             i = cols.pop();
49447             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49448         }
49449         this.updateColumns();
49450         this.layout();
49451     },
49452
49453     onRowSelect : function(rowIndex){
49454         var row = this.getRowComposite(rowIndex);
49455         row.addClass("x-grid-row-selected");
49456     },
49457
49458     onRowDeselect : function(rowIndex){
49459         var row = this.getRowComposite(rowIndex);
49460         row.removeClass("x-grid-row-selected");
49461     },
49462
49463     onCellSelect : function(row, col){
49464         var cell = this.getCell(row, col);
49465         if(cell){
49466             Roo.fly(cell).addClass("x-grid-cell-selected");
49467         }
49468     },
49469
49470     onCellDeselect : function(row, col){
49471         var cell = this.getCell(row, col);
49472         if(cell){
49473             Roo.fly(cell).removeClass("x-grid-cell-selected");
49474         }
49475     },
49476
49477     updateHeaderSortState : function(){
49478         
49479         // sort state can be single { field: xxx, direction : yyy}
49480         // or   { xxx=>ASC , yyy : DESC ..... }
49481         
49482         var mstate = {};
49483         if (!this.ds.multiSort) { 
49484             var state = this.ds.getSortState();
49485             if(!state){
49486                 return;
49487             }
49488             mstate[state.field] = state.direction;
49489             // FIXME... - this is not used here.. but might be elsewhere..
49490             this.sortState = state;
49491             
49492         } else {
49493             mstate = this.ds.sortToggle;
49494         }
49495         //remove existing sort classes..
49496         
49497         var sc = this.sortClasses;
49498         var hds = this.el.select(this.headerSelector).removeClass(sc);
49499         
49500         for(var f in mstate) {
49501         
49502             var sortColumn = this.cm.findColumnIndex(f);
49503             
49504             if(sortColumn != -1){
49505                 var sortDir = mstate[f];        
49506                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49507             }
49508         }
49509         
49510          
49511         
49512     },
49513
49514
49515     handleHeaderClick : function(g, index){
49516         if(this.headersDisabled){
49517             return;
49518         }
49519         var dm = g.dataSource, cm = g.colModel;
49520         if(!cm.isSortable(index)){
49521             return;
49522         }
49523         g.stopEditing();
49524         
49525         if (dm.multiSort) {
49526             // update the sortOrder
49527             var so = [];
49528             for(var i = 0; i < cm.config.length; i++ ) {
49529                 
49530                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49531                     continue; // dont' bother, it's not in sort list or being set.
49532                 }
49533                 
49534                 so.push(cm.config[i].dataIndex);
49535             };
49536             dm.sortOrder = so;
49537         }
49538         
49539         
49540         dm.sort(cm.getDataIndex(index));
49541     },
49542
49543
49544     destroy : function(){
49545         if(this.colMenu){
49546             this.colMenu.removeAll();
49547             Roo.menu.MenuMgr.unregister(this.colMenu);
49548             this.colMenu.getEl().remove();
49549             delete this.colMenu;
49550         }
49551         if(this.hmenu){
49552             this.hmenu.removeAll();
49553             Roo.menu.MenuMgr.unregister(this.hmenu);
49554             this.hmenu.getEl().remove();
49555             delete this.hmenu;
49556         }
49557         if(this.grid.enableColumnMove){
49558             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49559             if(dds){
49560                 for(var dd in dds){
49561                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49562                         var elid = dds[dd].dragElId;
49563                         dds[dd].unreg();
49564                         Roo.get(elid).remove();
49565                     } else if(dds[dd].config.isTarget){
49566                         dds[dd].proxyTop.remove();
49567                         dds[dd].proxyBottom.remove();
49568                         dds[dd].unreg();
49569                     }
49570                     if(Roo.dd.DDM.locationCache[dd]){
49571                         delete Roo.dd.DDM.locationCache[dd];
49572                     }
49573                 }
49574                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49575             }
49576         }
49577         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49578         this.bind(null, null);
49579         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49580     },
49581
49582     handleLockChange : function(){
49583         this.refresh(true);
49584     },
49585
49586     onDenyColumnLock : function(){
49587
49588     },
49589
49590     onDenyColumnHide : function(){
49591
49592     },
49593
49594     handleHdMenuClick : function(item){
49595         var index = this.hdCtxIndex;
49596         var cm = this.cm, ds = this.ds;
49597         switch(item.id){
49598             case "asc":
49599                 ds.sort(cm.getDataIndex(index), "ASC");
49600                 break;
49601             case "desc":
49602                 ds.sort(cm.getDataIndex(index), "DESC");
49603                 break;
49604             case "lock":
49605                 var lc = cm.getLockedCount();
49606                 if(cm.getColumnCount(true) <= lc+1){
49607                     this.onDenyColumnLock();
49608                     return;
49609                 }
49610                 if(lc != index){
49611                     cm.setLocked(index, true, true);
49612                     cm.moveColumn(index, lc);
49613                     this.grid.fireEvent("columnmove", index, lc);
49614                 }else{
49615                     cm.setLocked(index, true);
49616                 }
49617             break;
49618             case "unlock":
49619                 var lc = cm.getLockedCount();
49620                 if((lc-1) != index){
49621                     cm.setLocked(index, false, true);
49622                     cm.moveColumn(index, lc-1);
49623                     this.grid.fireEvent("columnmove", index, lc-1);
49624                 }else{
49625                     cm.setLocked(index, false);
49626                 }
49627             break;
49628             default:
49629                 index = cm.getIndexById(item.id.substr(4));
49630                 if(index != -1){
49631                     if(item.checked && cm.getColumnCount(true) <= 1){
49632                         this.onDenyColumnHide();
49633                         return false;
49634                     }
49635                     cm.setHidden(index, item.checked);
49636                 }
49637         }
49638         return true;
49639     },
49640
49641     beforeColMenuShow : function(){
49642         var cm = this.cm,  colCount = cm.getColumnCount();
49643         this.colMenu.removeAll();
49644         for(var i = 0; i < colCount; i++){
49645             this.colMenu.add(new Roo.menu.CheckItem({
49646                 id: "col-"+cm.getColumnId(i),
49647                 text: cm.getColumnHeader(i),
49648                 checked: !cm.isHidden(i),
49649                 hideOnClick:false
49650             }));
49651         }
49652     },
49653
49654     handleHdCtx : function(g, index, e){
49655         e.stopEvent();
49656         var hd = this.getHeaderCell(index);
49657         this.hdCtxIndex = index;
49658         var ms = this.hmenu.items, cm = this.cm;
49659         ms.get("asc").setDisabled(!cm.isSortable(index));
49660         ms.get("desc").setDisabled(!cm.isSortable(index));
49661         if(this.grid.enableColLock !== false){
49662             ms.get("lock").setDisabled(cm.isLocked(index));
49663             ms.get("unlock").setDisabled(!cm.isLocked(index));
49664         }
49665         this.hmenu.show(hd, "tl-bl");
49666     },
49667
49668     handleHdOver : function(e){
49669         var hd = this.findHeaderCell(e.getTarget());
49670         if(hd && !this.headersDisabled){
49671             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49672                this.fly(hd).addClass("x-grid-hd-over");
49673             }
49674         }
49675     },
49676
49677     handleHdOut : function(e){
49678         var hd = this.findHeaderCell(e.getTarget());
49679         if(hd){
49680             this.fly(hd).removeClass("x-grid-hd-over");
49681         }
49682     },
49683
49684     handleSplitDblClick : function(e, t){
49685         var i = this.getCellIndex(t);
49686         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49687             this.autoSizeColumn(i, true);
49688             this.layout();
49689         }
49690     },
49691
49692     render : function(){
49693
49694         var cm = this.cm;
49695         var colCount = cm.getColumnCount();
49696
49697         if(this.grid.monitorWindowResize === true){
49698             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49699         }
49700         var header = this.renderHeaders();
49701         var body = this.templates.body.apply({rows:""});
49702         var html = this.templates.master.apply({
49703             lockedBody: body,
49704             body: body,
49705             lockedHeader: header[0],
49706             header: header[1]
49707         });
49708
49709         //this.updateColumns();
49710
49711         this.grid.getGridEl().dom.innerHTML = html;
49712
49713         this.initElements();
49714         
49715         // a kludge to fix the random scolling effect in webkit
49716         this.el.on("scroll", function() {
49717             this.el.dom.scrollTop=0; // hopefully not recursive..
49718         },this);
49719
49720         this.scroller.on("scroll", this.handleScroll, this);
49721         this.lockedBody.on("mousewheel", this.handleWheel, this);
49722         this.mainBody.on("mousewheel", this.handleWheel, this);
49723
49724         this.mainHd.on("mouseover", this.handleHdOver, this);
49725         this.mainHd.on("mouseout", this.handleHdOut, this);
49726         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
49727                 {delegate: "."+this.splitClass});
49728
49729         this.lockedHd.on("mouseover", this.handleHdOver, this);
49730         this.lockedHd.on("mouseout", this.handleHdOut, this);
49731         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
49732                 {delegate: "."+this.splitClass});
49733
49734         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
49735             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49736         }
49737
49738         this.updateSplitters();
49739
49740         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
49741             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49742             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49743         }
49744
49745         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
49746             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
49747             this.hmenu.add(
49748                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
49749                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
49750             );
49751             if(this.grid.enableColLock !== false){
49752                 this.hmenu.add('-',
49753                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
49754                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
49755                 );
49756             }
49757             if(this.grid.enableColumnHide !== false){
49758
49759                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
49760                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
49761                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
49762
49763                 this.hmenu.add('-',
49764                     {id:"columns", text: this.columnsText, menu: this.colMenu}
49765                 );
49766             }
49767             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
49768
49769             this.grid.on("headercontextmenu", this.handleHdCtx, this);
49770         }
49771
49772         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
49773             this.dd = new Roo.grid.GridDragZone(this.grid, {
49774                 ddGroup : this.grid.ddGroup || 'GridDD'
49775             });
49776         }
49777
49778         /*
49779         for(var i = 0; i < colCount; i++){
49780             if(cm.isHidden(i)){
49781                 this.hideColumn(i);
49782             }
49783             if(cm.config[i].align){
49784                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
49785                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
49786             }
49787         }*/
49788         
49789         this.updateHeaderSortState();
49790
49791         this.beforeInitialResize();
49792         this.layout(true);
49793
49794         // two part rendering gives faster view to the user
49795         this.renderPhase2.defer(1, this);
49796     },
49797
49798     renderPhase2 : function(){
49799         // render the rows now
49800         this.refresh();
49801         if(this.grid.autoSizeColumns){
49802             this.autoSizeColumns();
49803         }
49804     },
49805
49806     beforeInitialResize : function(){
49807
49808     },
49809
49810     onColumnSplitterMoved : function(i, w){
49811         this.userResized = true;
49812         var cm = this.grid.colModel;
49813         cm.setColumnWidth(i, w, true);
49814         var cid = cm.getColumnId(i);
49815         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49816         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49817         this.updateSplitters();
49818         this.layout();
49819         this.grid.fireEvent("columnresize", i, w);
49820     },
49821
49822     syncRowHeights : function(startIndex, endIndex){
49823         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
49824             startIndex = startIndex || 0;
49825             var mrows = this.getBodyTable().rows;
49826             var lrows = this.getLockedTable().rows;
49827             var len = mrows.length-1;
49828             endIndex = Math.min(endIndex || len, len);
49829             for(var i = startIndex; i <= endIndex; i++){
49830                 var m = mrows[i], l = lrows[i];
49831                 var h = Math.max(m.offsetHeight, l.offsetHeight);
49832                 m.style.height = l.style.height = h + "px";
49833             }
49834         }
49835     },
49836
49837     layout : function(initialRender, is2ndPass){
49838         var g = this.grid;
49839         var auto = g.autoHeight;
49840         var scrollOffset = 16;
49841         var c = g.getGridEl(), cm = this.cm,
49842                 expandCol = g.autoExpandColumn,
49843                 gv = this;
49844         //c.beginMeasure();
49845
49846         if(!c.dom.offsetWidth){ // display:none?
49847             if(initialRender){
49848                 this.lockedWrap.show();
49849                 this.mainWrap.show();
49850             }
49851             return;
49852         }
49853
49854         var hasLock = this.cm.isLocked(0);
49855
49856         var tbh = this.headerPanel.getHeight();
49857         var bbh = this.footerPanel.getHeight();
49858
49859         if(auto){
49860             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
49861             var newHeight = ch + c.getBorderWidth("tb");
49862             if(g.maxHeight){
49863                 newHeight = Math.min(g.maxHeight, newHeight);
49864             }
49865             c.setHeight(newHeight);
49866         }
49867
49868         if(g.autoWidth){
49869             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
49870         }
49871
49872         var s = this.scroller;
49873
49874         var csize = c.getSize(true);
49875
49876         this.el.setSize(csize.width, csize.height);
49877
49878         this.headerPanel.setWidth(csize.width);
49879         this.footerPanel.setWidth(csize.width);
49880
49881         var hdHeight = this.mainHd.getHeight();
49882         var vw = csize.width;
49883         var vh = csize.height - (tbh + bbh);
49884
49885         s.setSize(vw, vh);
49886
49887         var bt = this.getBodyTable();
49888         var ltWidth = hasLock ?
49889                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
49890
49891         var scrollHeight = bt.offsetHeight;
49892         var scrollWidth = ltWidth + bt.offsetWidth;
49893         var vscroll = false, hscroll = false;
49894
49895         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
49896
49897         var lw = this.lockedWrap, mw = this.mainWrap;
49898         var lb = this.lockedBody, mb = this.mainBody;
49899
49900         setTimeout(function(){
49901             var t = s.dom.offsetTop;
49902             var w = s.dom.clientWidth,
49903                 h = s.dom.clientHeight;
49904
49905             lw.setTop(t);
49906             lw.setSize(ltWidth, h);
49907
49908             mw.setLeftTop(ltWidth, t);
49909             mw.setSize(w-ltWidth, h);
49910
49911             lb.setHeight(h-hdHeight);
49912             mb.setHeight(h-hdHeight);
49913
49914             if(is2ndPass !== true && !gv.userResized && expandCol){
49915                 // high speed resize without full column calculation
49916                 
49917                 var ci = cm.getIndexById(expandCol);
49918                 if (ci < 0) {
49919                     ci = cm.findColumnIndex(expandCol);
49920                 }
49921                 ci = Math.max(0, ci); // make sure it's got at least the first col.
49922                 var expandId = cm.getColumnId(ci);
49923                 var  tw = cm.getTotalWidth(false);
49924                 var currentWidth = cm.getColumnWidth(ci);
49925                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
49926                 if(currentWidth != cw){
49927                     cm.setColumnWidth(ci, cw, true);
49928                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49929                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49930                     gv.updateSplitters();
49931                     gv.layout(false, true);
49932                 }
49933             }
49934
49935             if(initialRender){
49936                 lw.show();
49937                 mw.show();
49938             }
49939             //c.endMeasure();
49940         }, 10);
49941     },
49942
49943     onWindowResize : function(){
49944         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
49945             return;
49946         }
49947         this.layout();
49948     },
49949
49950     appendFooter : function(parentEl){
49951         return null;
49952     },
49953
49954     sortAscText : "Sort Ascending",
49955     sortDescText : "Sort Descending",
49956     lockText : "Lock Column",
49957     unlockText : "Unlock Column",
49958     columnsText : "Columns"
49959 });
49960
49961
49962 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
49963     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
49964     this.proxy.el.addClass('x-grid3-col-dd');
49965 };
49966
49967 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
49968     handleMouseDown : function(e){
49969
49970     },
49971
49972     callHandleMouseDown : function(e){
49973         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
49974     }
49975 });
49976 /*
49977  * Based on:
49978  * Ext JS Library 1.1.1
49979  * Copyright(c) 2006-2007, Ext JS, LLC.
49980  *
49981  * Originally Released Under LGPL - original licence link has changed is not relivant.
49982  *
49983  * Fork - LGPL
49984  * <script type="text/javascript">
49985  */
49986  
49987 // private
49988 // This is a support class used internally by the Grid components
49989 Roo.grid.SplitDragZone = function(grid, hd, hd2){
49990     this.grid = grid;
49991     this.view = grid.getView();
49992     this.proxy = this.view.resizeProxy;
49993     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
49994         "gridSplitters" + this.grid.getGridEl().id, {
49995         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
49996     });
49997     this.setHandleElId(Roo.id(hd));
49998     this.setOuterHandleElId(Roo.id(hd2));
49999     this.scroll = false;
50000 };
50001 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
50002     fly: Roo.Element.fly,
50003
50004     b4StartDrag : function(x, y){
50005         this.view.headersDisabled = true;
50006         this.proxy.setHeight(this.view.mainWrap.getHeight());
50007         var w = this.cm.getColumnWidth(this.cellIndex);
50008         var minw = Math.max(w-this.grid.minColumnWidth, 0);
50009         this.resetConstraints();
50010         this.setXConstraint(minw, 1000);
50011         this.setYConstraint(0, 0);
50012         this.minX = x - minw;
50013         this.maxX = x + 1000;
50014         this.startPos = x;
50015         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
50016     },
50017
50018
50019     handleMouseDown : function(e){
50020         ev = Roo.EventObject.setEvent(e);
50021         var t = this.fly(ev.getTarget());
50022         if(t.hasClass("x-grid-split")){
50023             this.cellIndex = this.view.getCellIndex(t.dom);
50024             this.split = t.dom;
50025             this.cm = this.grid.colModel;
50026             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
50027                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
50028             }
50029         }
50030     },
50031
50032     endDrag : function(e){
50033         this.view.headersDisabled = false;
50034         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
50035         var diff = endX - this.startPos;
50036         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
50037     },
50038
50039     autoOffset : function(){
50040         this.setDelta(0,0);
50041     }
50042 });/*
50043  * Based on:
50044  * Ext JS Library 1.1.1
50045  * Copyright(c) 2006-2007, Ext JS, LLC.
50046  *
50047  * Originally Released Under LGPL - original licence link has changed is not relivant.
50048  *
50049  * Fork - LGPL
50050  * <script type="text/javascript">
50051  */
50052  
50053 // private
50054 // This is a support class used internally by the Grid components
50055 Roo.grid.GridDragZone = function(grid, config){
50056     this.view = grid.getView();
50057     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
50058     if(this.view.lockedBody){
50059         this.setHandleElId(Roo.id(this.view.mainBody.dom));
50060         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
50061     }
50062     this.scroll = false;
50063     this.grid = grid;
50064     this.ddel = document.createElement('div');
50065     this.ddel.className = 'x-grid-dd-wrap';
50066 };
50067
50068 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50069     ddGroup : "GridDD",
50070
50071     getDragData : function(e){
50072         var t = Roo.lib.Event.getTarget(e);
50073         var rowIndex = this.view.findRowIndex(t);
50074         if(rowIndex !== false){
50075             var sm = this.grid.selModel;
50076             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50077               //  sm.mouseDown(e, t);
50078             //}
50079             if (e.hasModifier()){
50080                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50081             }
50082             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50083         }
50084         return false;
50085     },
50086
50087     onInitDrag : function(e){
50088         var data = this.dragData;
50089         this.ddel.innerHTML = this.grid.getDragDropText();
50090         this.proxy.update(this.ddel);
50091         // fire start drag?
50092     },
50093
50094     afterRepair : function(){
50095         this.dragging = false;
50096     },
50097
50098     getRepairXY : function(e, data){
50099         return false;
50100     },
50101
50102     onEndDrag : function(data, e){
50103         // fire end drag?
50104     },
50105
50106     onValidDrop : function(dd, e, id){
50107         // fire drag drop?
50108         this.hideProxy();
50109     },
50110
50111     beforeInvalidDrop : function(e, id){
50112
50113     }
50114 });/*
50115  * Based on:
50116  * Ext JS Library 1.1.1
50117  * Copyright(c) 2006-2007, Ext JS, LLC.
50118  *
50119  * Originally Released Under LGPL - original licence link has changed is not relivant.
50120  *
50121  * Fork - LGPL
50122  * <script type="text/javascript">
50123  */
50124  
50125
50126 /**
50127  * @class Roo.grid.ColumnModel
50128  * @extends Roo.util.Observable
50129  * This is the default implementation of a ColumnModel used by the Grid. It defines
50130  * the columns in the grid.
50131  * <br>Usage:<br>
50132  <pre><code>
50133  var colModel = new Roo.grid.ColumnModel([
50134         {header: "Ticker", width: 60, sortable: true, locked: true},
50135         {header: "Company Name", width: 150, sortable: true},
50136         {header: "Market Cap.", width: 100, sortable: true},
50137         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50138         {header: "Employees", width: 100, sortable: true, resizable: false}
50139  ]);
50140  </code></pre>
50141  * <p>
50142  
50143  * The config options listed for this class are options which may appear in each
50144  * individual column definition.
50145  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50146  * @constructor
50147  * @param {Object} config An Array of column config objects. See this class's
50148  * config objects for details.
50149 */
50150 Roo.grid.ColumnModel = function(config){
50151         /**
50152      * The config passed into the constructor
50153      */
50154     this.config = config;
50155     this.lookup = {};
50156
50157     // if no id, create one
50158     // if the column does not have a dataIndex mapping,
50159     // map it to the order it is in the config
50160     for(var i = 0, len = config.length; i < len; i++){
50161         var c = config[i];
50162         if(typeof c.dataIndex == "undefined"){
50163             c.dataIndex = i;
50164         }
50165         if(typeof c.renderer == "string"){
50166             c.renderer = Roo.util.Format[c.renderer];
50167         }
50168         if(typeof c.id == "undefined"){
50169             c.id = Roo.id();
50170         }
50171         if(c.editor && c.editor.xtype){
50172             c.editor  = Roo.factory(c.editor, Roo.grid);
50173         }
50174         if(c.editor && c.editor.isFormField){
50175             c.editor = new Roo.grid.GridEditor(c.editor);
50176         }
50177         this.lookup[c.id] = c;
50178     }
50179
50180     /**
50181      * The width of columns which have no width specified (defaults to 100)
50182      * @type Number
50183      */
50184     this.defaultWidth = 100;
50185
50186     /**
50187      * Default sortable of columns which have no sortable specified (defaults to false)
50188      * @type Boolean
50189      */
50190     this.defaultSortable = false;
50191
50192     this.addEvents({
50193         /**
50194              * @event widthchange
50195              * Fires when the width of a column changes.
50196              * @param {ColumnModel} this
50197              * @param {Number} columnIndex The column index
50198              * @param {Number} newWidth The new width
50199              */
50200             "widthchange": true,
50201         /**
50202              * @event headerchange
50203              * Fires when the text of a header changes.
50204              * @param {ColumnModel} this
50205              * @param {Number} columnIndex The column index
50206              * @param {Number} newText The new header text
50207              */
50208             "headerchange": true,
50209         /**
50210              * @event hiddenchange
50211              * Fires when a column is hidden or "unhidden".
50212              * @param {ColumnModel} this
50213              * @param {Number} columnIndex The column index
50214              * @param {Boolean} hidden true if hidden, false otherwise
50215              */
50216             "hiddenchange": true,
50217             /**
50218          * @event columnmoved
50219          * Fires when a column is moved.
50220          * @param {ColumnModel} this
50221          * @param {Number} oldIndex
50222          * @param {Number} newIndex
50223          */
50224         "columnmoved" : true,
50225         /**
50226          * @event columlockchange
50227          * Fires when a column's locked state is changed
50228          * @param {ColumnModel} this
50229          * @param {Number} colIndex
50230          * @param {Boolean} locked true if locked
50231          */
50232         "columnlockchange" : true
50233     });
50234     Roo.grid.ColumnModel.superclass.constructor.call(this);
50235 };
50236 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50237     /**
50238      * @cfg {String} header The header text to display in the Grid view.
50239      */
50240     /**
50241      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50242      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50243      * specified, the column's index is used as an index into the Record's data Array.
50244      */
50245     /**
50246      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50247      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50248      */
50249     /**
50250      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50251      * Defaults to the value of the {@link #defaultSortable} property.
50252      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50253      */
50254     /**
50255      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50256      */
50257     /**
50258      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50259      */
50260     /**
50261      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50262      */
50263     /**
50264      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50265      */
50266     /**
50267      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50268      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50269      * default renderer uses the raw data value.
50270      */
50271        /**
50272      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
50273      */
50274     /**
50275      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
50276      */
50277
50278     /**
50279      * Returns the id of the column at the specified index.
50280      * @param {Number} index The column index
50281      * @return {String} the id
50282      */
50283     getColumnId : function(index){
50284         return this.config[index].id;
50285     },
50286
50287     /**
50288      * Returns the column for a specified id.
50289      * @param {String} id The column id
50290      * @return {Object} the column
50291      */
50292     getColumnById : function(id){
50293         return this.lookup[id];
50294     },
50295
50296     
50297     /**
50298      * Returns the column for a specified dataIndex.
50299      * @param {String} dataIndex The column dataIndex
50300      * @return {Object|Boolean} the column or false if not found
50301      */
50302     getColumnByDataIndex: function(dataIndex){
50303         var index = this.findColumnIndex(dataIndex);
50304         return index > -1 ? this.config[index] : false;
50305     },
50306     
50307     /**
50308      * Returns the index for a specified column id.
50309      * @param {String} id The column id
50310      * @return {Number} the index, or -1 if not found
50311      */
50312     getIndexById : function(id){
50313         for(var i = 0, len = this.config.length; i < len; i++){
50314             if(this.config[i].id == id){
50315                 return i;
50316             }
50317         }
50318         return -1;
50319     },
50320     
50321     /**
50322      * Returns the index for a specified column dataIndex.
50323      * @param {String} dataIndex The column dataIndex
50324      * @return {Number} the index, or -1 if not found
50325      */
50326     
50327     findColumnIndex : function(dataIndex){
50328         for(var i = 0, len = this.config.length; i < len; i++){
50329             if(this.config[i].dataIndex == dataIndex){
50330                 return i;
50331             }
50332         }
50333         return -1;
50334     },
50335     
50336     
50337     moveColumn : function(oldIndex, newIndex){
50338         var c = this.config[oldIndex];
50339         this.config.splice(oldIndex, 1);
50340         this.config.splice(newIndex, 0, c);
50341         this.dataMap = null;
50342         this.fireEvent("columnmoved", this, oldIndex, newIndex);
50343     },
50344
50345     isLocked : function(colIndex){
50346         return this.config[colIndex].locked === true;
50347     },
50348
50349     setLocked : function(colIndex, value, suppressEvent){
50350         if(this.isLocked(colIndex) == value){
50351             return;
50352         }
50353         this.config[colIndex].locked = value;
50354         if(!suppressEvent){
50355             this.fireEvent("columnlockchange", this, colIndex, value);
50356         }
50357     },
50358
50359     getTotalLockedWidth : function(){
50360         var totalWidth = 0;
50361         for(var i = 0; i < this.config.length; i++){
50362             if(this.isLocked(i) && !this.isHidden(i)){
50363                 this.totalWidth += this.getColumnWidth(i);
50364             }
50365         }
50366         return totalWidth;
50367     },
50368
50369     getLockedCount : function(){
50370         for(var i = 0, len = this.config.length; i < len; i++){
50371             if(!this.isLocked(i)){
50372                 return i;
50373             }
50374         }
50375     },
50376
50377     /**
50378      * Returns the number of columns.
50379      * @return {Number}
50380      */
50381     getColumnCount : function(visibleOnly){
50382         if(visibleOnly === true){
50383             var c = 0;
50384             for(var i = 0, len = this.config.length; i < len; i++){
50385                 if(!this.isHidden(i)){
50386                     c++;
50387                 }
50388             }
50389             return c;
50390         }
50391         return this.config.length;
50392     },
50393
50394     /**
50395      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
50396      * @param {Function} fn
50397      * @param {Object} scope (optional)
50398      * @return {Array} result
50399      */
50400     getColumnsBy : function(fn, scope){
50401         var r = [];
50402         for(var i = 0, len = this.config.length; i < len; i++){
50403             var c = this.config[i];
50404             if(fn.call(scope||this, c, i) === true){
50405                 r[r.length] = c;
50406             }
50407         }
50408         return r;
50409     },
50410
50411     /**
50412      * Returns true if the specified column is sortable.
50413      * @param {Number} col The column index
50414      * @return {Boolean}
50415      */
50416     isSortable : function(col){
50417         if(typeof this.config[col].sortable == "undefined"){
50418             return this.defaultSortable;
50419         }
50420         return this.config[col].sortable;
50421     },
50422
50423     /**
50424      * Returns the rendering (formatting) function defined for the column.
50425      * @param {Number} col The column index.
50426      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50427      */
50428     getRenderer : function(col){
50429         if(!this.config[col].renderer){
50430             return Roo.grid.ColumnModel.defaultRenderer;
50431         }
50432         return this.config[col].renderer;
50433     },
50434
50435     /**
50436      * Sets the rendering (formatting) function for a column.
50437      * @param {Number} col The column index
50438      * @param {Function} fn The function to use to process the cell's raw data
50439      * to return HTML markup for the grid view. The render function is called with
50440      * the following parameters:<ul>
50441      * <li>Data value.</li>
50442      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50443      * <li>css A CSS style string to apply to the table cell.</li>
50444      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50445      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50446      * <li>Row index</li>
50447      * <li>Column index</li>
50448      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50449      */
50450     setRenderer : function(col, fn){
50451         this.config[col].renderer = fn;
50452     },
50453
50454     /**
50455      * Returns the width for the specified column.
50456      * @param {Number} col The column index
50457      * @return {Number}
50458      */
50459     getColumnWidth : function(col){
50460         return this.config[col].width * 1 || this.defaultWidth;
50461     },
50462
50463     /**
50464      * Sets the width for a column.
50465      * @param {Number} col The column index
50466      * @param {Number} width The new width
50467      */
50468     setColumnWidth : function(col, width, suppressEvent){
50469         this.config[col].width = width;
50470         this.totalWidth = null;
50471         if(!suppressEvent){
50472              this.fireEvent("widthchange", this, col, width);
50473         }
50474     },
50475
50476     /**
50477      * Returns the total width of all columns.
50478      * @param {Boolean} includeHidden True to include hidden column widths
50479      * @return {Number}
50480      */
50481     getTotalWidth : function(includeHidden){
50482         if(!this.totalWidth){
50483             this.totalWidth = 0;
50484             for(var i = 0, len = this.config.length; i < len; i++){
50485                 if(includeHidden || !this.isHidden(i)){
50486                     this.totalWidth += this.getColumnWidth(i);
50487                 }
50488             }
50489         }
50490         return this.totalWidth;
50491     },
50492
50493     /**
50494      * Returns the header for the specified column.
50495      * @param {Number} col The column index
50496      * @return {String}
50497      */
50498     getColumnHeader : function(col){
50499         return this.config[col].header;
50500     },
50501
50502     /**
50503      * Sets the header for a column.
50504      * @param {Number} col The column index
50505      * @param {String} header The new header
50506      */
50507     setColumnHeader : function(col, header){
50508         this.config[col].header = header;
50509         this.fireEvent("headerchange", this, col, header);
50510     },
50511
50512     /**
50513      * Returns the tooltip for the specified column.
50514      * @param {Number} col The column index
50515      * @return {String}
50516      */
50517     getColumnTooltip : function(col){
50518             return this.config[col].tooltip;
50519     },
50520     /**
50521      * Sets the tooltip for a column.
50522      * @param {Number} col The column index
50523      * @param {String} tooltip The new tooltip
50524      */
50525     setColumnTooltip : function(col, tooltip){
50526             this.config[col].tooltip = tooltip;
50527     },
50528
50529     /**
50530      * Returns the dataIndex for the specified column.
50531      * @param {Number} col The column index
50532      * @return {Number}
50533      */
50534     getDataIndex : function(col){
50535         return this.config[col].dataIndex;
50536     },
50537
50538     /**
50539      * Sets the dataIndex for a column.
50540      * @param {Number} col The column index
50541      * @param {Number} dataIndex The new dataIndex
50542      */
50543     setDataIndex : function(col, dataIndex){
50544         this.config[col].dataIndex = dataIndex;
50545     },
50546
50547     
50548     
50549     /**
50550      * Returns true if the cell is editable.
50551      * @param {Number} colIndex The column index
50552      * @param {Number} rowIndex The row index
50553      * @return {Boolean}
50554      */
50555     isCellEditable : function(colIndex, rowIndex){
50556         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50557     },
50558
50559     /**
50560      * Returns the editor defined for the cell/column.
50561      * return false or null to disable editing.
50562      * @param {Number} colIndex The column index
50563      * @param {Number} rowIndex The row index
50564      * @return {Object}
50565      */
50566     getCellEditor : function(colIndex, rowIndex){
50567         return this.config[colIndex].editor;
50568     },
50569
50570     /**
50571      * Sets if a column is editable.
50572      * @param {Number} col The column index
50573      * @param {Boolean} editable True if the column is editable
50574      */
50575     setEditable : function(col, editable){
50576         this.config[col].editable = editable;
50577     },
50578
50579
50580     /**
50581      * Returns true if the column is hidden.
50582      * @param {Number} colIndex The column index
50583      * @return {Boolean}
50584      */
50585     isHidden : function(colIndex){
50586         return this.config[colIndex].hidden;
50587     },
50588
50589
50590     /**
50591      * Returns true if the column width cannot be changed
50592      */
50593     isFixed : function(colIndex){
50594         return this.config[colIndex].fixed;
50595     },
50596
50597     /**
50598      * Returns true if the column can be resized
50599      * @return {Boolean}
50600      */
50601     isResizable : function(colIndex){
50602         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50603     },
50604     /**
50605      * Sets if a column is hidden.
50606      * @param {Number} colIndex The column index
50607      * @param {Boolean} hidden True if the column is hidden
50608      */
50609     setHidden : function(colIndex, hidden){
50610         this.config[colIndex].hidden = hidden;
50611         this.totalWidth = null;
50612         this.fireEvent("hiddenchange", this, colIndex, hidden);
50613     },
50614
50615     /**
50616      * Sets the editor for a column.
50617      * @param {Number} col The column index
50618      * @param {Object} editor The editor object
50619      */
50620     setEditor : function(col, editor){
50621         this.config[col].editor = editor;
50622     }
50623 });
50624
50625 Roo.grid.ColumnModel.defaultRenderer = function(value){
50626         if(typeof value == "string" && value.length < 1){
50627             return "&#160;";
50628         }
50629         return value;
50630 };
50631
50632 // Alias for backwards compatibility
50633 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50634 /*
50635  * Based on:
50636  * Ext JS Library 1.1.1
50637  * Copyright(c) 2006-2007, Ext JS, LLC.
50638  *
50639  * Originally Released Under LGPL - original licence link has changed is not relivant.
50640  *
50641  * Fork - LGPL
50642  * <script type="text/javascript">
50643  */
50644
50645 /**
50646  * @class Roo.grid.AbstractSelectionModel
50647  * @extends Roo.util.Observable
50648  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50649  * implemented by descendant classes.  This class should not be directly instantiated.
50650  * @constructor
50651  */
50652 Roo.grid.AbstractSelectionModel = function(){
50653     this.locked = false;
50654     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50655 };
50656
50657 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50658     /** @ignore Called by the grid automatically. Do not call directly. */
50659     init : function(grid){
50660         this.grid = grid;
50661         this.initEvents();
50662     },
50663
50664     /**
50665      * Locks the selections.
50666      */
50667     lock : function(){
50668         this.locked = true;
50669     },
50670
50671     /**
50672      * Unlocks the selections.
50673      */
50674     unlock : function(){
50675         this.locked = false;
50676     },
50677
50678     /**
50679      * Returns true if the selections are locked.
50680      * @return {Boolean}
50681      */
50682     isLocked : function(){
50683         return this.locked;
50684     }
50685 });/*
50686  * Based on:
50687  * Ext JS Library 1.1.1
50688  * Copyright(c) 2006-2007, Ext JS, LLC.
50689  *
50690  * Originally Released Under LGPL - original licence link has changed is not relivant.
50691  *
50692  * Fork - LGPL
50693  * <script type="text/javascript">
50694  */
50695 /**
50696  * @extends Roo.grid.AbstractSelectionModel
50697  * @class Roo.grid.RowSelectionModel
50698  * The default SelectionModel used by {@link Roo.grid.Grid}.
50699  * It supports multiple selections and keyboard selection/navigation. 
50700  * @constructor
50701  * @param {Object} config
50702  */
50703 Roo.grid.RowSelectionModel = function(config){
50704     Roo.apply(this, config);
50705     this.selections = new Roo.util.MixedCollection(false, function(o){
50706         return o.id;
50707     });
50708
50709     this.last = false;
50710     this.lastActive = false;
50711
50712     this.addEvents({
50713         /**
50714              * @event selectionchange
50715              * Fires when the selection changes
50716              * @param {SelectionModel} this
50717              */
50718             "selectionchange" : true,
50719         /**
50720              * @event afterselectionchange
50721              * Fires after the selection changes (eg. by key press or clicking)
50722              * @param {SelectionModel} this
50723              */
50724             "afterselectionchange" : true,
50725         /**
50726              * @event beforerowselect
50727              * Fires when a row is selected being selected, return false to cancel.
50728              * @param {SelectionModel} this
50729              * @param {Number} rowIndex The selected index
50730              * @param {Boolean} keepExisting False if other selections will be cleared
50731              */
50732             "beforerowselect" : true,
50733         /**
50734              * @event rowselect
50735              * Fires when a row is selected.
50736              * @param {SelectionModel} this
50737              * @param {Number} rowIndex The selected index
50738              * @param {Roo.data.Record} r The record
50739              */
50740             "rowselect" : true,
50741         /**
50742              * @event rowdeselect
50743              * Fires when a row is deselected.
50744              * @param {SelectionModel} this
50745              * @param {Number} rowIndex The selected index
50746              */
50747         "rowdeselect" : true
50748     });
50749     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
50750     this.locked = false;
50751 };
50752
50753 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
50754     /**
50755      * @cfg {Boolean} singleSelect
50756      * True to allow selection of only one row at a time (defaults to false)
50757      */
50758     singleSelect : false,
50759
50760     // private
50761     initEvents : function(){
50762
50763         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
50764             this.grid.on("mousedown", this.handleMouseDown, this);
50765         }else{ // allow click to work like normal
50766             this.grid.on("rowclick", this.handleDragableRowClick, this);
50767         }
50768
50769         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
50770             "up" : function(e){
50771                 if(!e.shiftKey){
50772                     this.selectPrevious(e.shiftKey);
50773                 }else if(this.last !== false && this.lastActive !== false){
50774                     var last = this.last;
50775                     this.selectRange(this.last,  this.lastActive-1);
50776                     this.grid.getView().focusRow(this.lastActive);
50777                     if(last !== false){
50778                         this.last = last;
50779                     }
50780                 }else{
50781                     this.selectFirstRow();
50782                 }
50783                 this.fireEvent("afterselectionchange", this);
50784             },
50785             "down" : function(e){
50786                 if(!e.shiftKey){
50787                     this.selectNext(e.shiftKey);
50788                 }else if(this.last !== false && this.lastActive !== false){
50789                     var last = this.last;
50790                     this.selectRange(this.last,  this.lastActive+1);
50791                     this.grid.getView().focusRow(this.lastActive);
50792                     if(last !== false){
50793                         this.last = last;
50794                     }
50795                 }else{
50796                     this.selectFirstRow();
50797                 }
50798                 this.fireEvent("afterselectionchange", this);
50799             },
50800             scope: this
50801         });
50802
50803         var view = this.grid.view;
50804         view.on("refresh", this.onRefresh, this);
50805         view.on("rowupdated", this.onRowUpdated, this);
50806         view.on("rowremoved", this.onRemove, this);
50807     },
50808
50809     // private
50810     onRefresh : function(){
50811         var ds = this.grid.dataSource, i, v = this.grid.view;
50812         var s = this.selections;
50813         s.each(function(r){
50814             if((i = ds.indexOfId(r.id)) != -1){
50815                 v.onRowSelect(i);
50816             }else{
50817                 s.remove(r);
50818             }
50819         });
50820     },
50821
50822     // private
50823     onRemove : function(v, index, r){
50824         this.selections.remove(r);
50825     },
50826
50827     // private
50828     onRowUpdated : function(v, index, r){
50829         if(this.isSelected(r)){
50830             v.onRowSelect(index);
50831         }
50832     },
50833
50834     /**
50835      * Select records.
50836      * @param {Array} records The records to select
50837      * @param {Boolean} keepExisting (optional) True to keep existing selections
50838      */
50839     selectRecords : function(records, keepExisting){
50840         if(!keepExisting){
50841             this.clearSelections();
50842         }
50843         var ds = this.grid.dataSource;
50844         for(var i = 0, len = records.length; i < len; i++){
50845             this.selectRow(ds.indexOf(records[i]), true);
50846         }
50847     },
50848
50849     /**
50850      * Gets the number of selected rows.
50851      * @return {Number}
50852      */
50853     getCount : function(){
50854         return this.selections.length;
50855     },
50856
50857     /**
50858      * Selects the first row in the grid.
50859      */
50860     selectFirstRow : function(){
50861         this.selectRow(0);
50862     },
50863
50864     /**
50865      * Select the last row.
50866      * @param {Boolean} keepExisting (optional) True to keep existing selections
50867      */
50868     selectLastRow : function(keepExisting){
50869         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
50870     },
50871
50872     /**
50873      * Selects the row immediately following the last selected row.
50874      * @param {Boolean} keepExisting (optional) True to keep existing selections
50875      */
50876     selectNext : function(keepExisting){
50877         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
50878             this.selectRow(this.last+1, keepExisting);
50879             this.grid.getView().focusRow(this.last);
50880         }
50881     },
50882
50883     /**
50884      * Selects the row that precedes the last selected row.
50885      * @param {Boolean} keepExisting (optional) True to keep existing selections
50886      */
50887     selectPrevious : function(keepExisting){
50888         if(this.last){
50889             this.selectRow(this.last-1, keepExisting);
50890             this.grid.getView().focusRow(this.last);
50891         }
50892     },
50893
50894     /**
50895      * Returns the selected records
50896      * @return {Array} Array of selected records
50897      */
50898     getSelections : function(){
50899         return [].concat(this.selections.items);
50900     },
50901
50902     /**
50903      * Returns the first selected record.
50904      * @return {Record}
50905      */
50906     getSelected : function(){
50907         return this.selections.itemAt(0);
50908     },
50909
50910
50911     /**
50912      * Clears all selections.
50913      */
50914     clearSelections : function(fast){
50915         if(this.locked) return;
50916         if(fast !== true){
50917             var ds = this.grid.dataSource;
50918             var s = this.selections;
50919             s.each(function(r){
50920                 this.deselectRow(ds.indexOfId(r.id));
50921             }, this);
50922             s.clear();
50923         }else{
50924             this.selections.clear();
50925         }
50926         this.last = false;
50927     },
50928
50929
50930     /**
50931      * Selects all rows.
50932      */
50933     selectAll : function(){
50934         if(this.locked) return;
50935         this.selections.clear();
50936         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
50937             this.selectRow(i, true);
50938         }
50939     },
50940
50941     /**
50942      * Returns True if there is a selection.
50943      * @return {Boolean}
50944      */
50945     hasSelection : function(){
50946         return this.selections.length > 0;
50947     },
50948
50949     /**
50950      * Returns True if the specified row is selected.
50951      * @param {Number/Record} record The record or index of the record to check
50952      * @return {Boolean}
50953      */
50954     isSelected : function(index){
50955         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
50956         return (r && this.selections.key(r.id) ? true : false);
50957     },
50958
50959     /**
50960      * Returns True if the specified record id is selected.
50961      * @param {String} id The id of record to check
50962      * @return {Boolean}
50963      */
50964     isIdSelected : function(id){
50965         return (this.selections.key(id) ? true : false);
50966     },
50967
50968     // private
50969     handleMouseDown : function(e, t){
50970         var view = this.grid.getView(), rowIndex;
50971         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
50972             return;
50973         };
50974         if(e.shiftKey && this.last !== false){
50975             var last = this.last;
50976             this.selectRange(last, rowIndex, e.ctrlKey);
50977             this.last = last; // reset the last
50978             view.focusRow(rowIndex);
50979         }else{
50980             var isSelected = this.isSelected(rowIndex);
50981             if(e.button !== 0 && isSelected){
50982                 view.focusRow(rowIndex);
50983             }else if(e.ctrlKey && isSelected){
50984                 this.deselectRow(rowIndex);
50985             }else if(!isSelected){
50986                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
50987                 view.focusRow(rowIndex);
50988             }
50989         }
50990         this.fireEvent("afterselectionchange", this);
50991     },
50992     // private
50993     handleDragableRowClick :  function(grid, rowIndex, e) 
50994     {
50995         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
50996             this.selectRow(rowIndex, false);
50997             grid.view.focusRow(rowIndex);
50998              this.fireEvent("afterselectionchange", this);
50999         }
51000     },
51001     
51002     /**
51003      * Selects multiple rows.
51004      * @param {Array} rows Array of the indexes of the row to select
51005      * @param {Boolean} keepExisting (optional) True to keep existing selections
51006      */
51007     selectRows : function(rows, keepExisting){
51008         if(!keepExisting){
51009             this.clearSelections();
51010         }
51011         for(var i = 0, len = rows.length; i < len; i++){
51012             this.selectRow(rows[i], true);
51013         }
51014     },
51015
51016     /**
51017      * Selects a range of rows. All rows in between startRow and endRow are also selected.
51018      * @param {Number} startRow The index of the first row in the range
51019      * @param {Number} endRow The index of the last row in the range
51020      * @param {Boolean} keepExisting (optional) True to retain existing selections
51021      */
51022     selectRange : function(startRow, endRow, keepExisting){
51023         if(this.locked) return;
51024         if(!keepExisting){
51025             this.clearSelections();
51026         }
51027         if(startRow <= endRow){
51028             for(var i = startRow; i <= endRow; i++){
51029                 this.selectRow(i, true);
51030             }
51031         }else{
51032             for(var i = startRow; i >= endRow; i--){
51033                 this.selectRow(i, true);
51034             }
51035         }
51036     },
51037
51038     /**
51039      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
51040      * @param {Number} startRow The index of the first row in the range
51041      * @param {Number} endRow The index of the last row in the range
51042      */
51043     deselectRange : function(startRow, endRow, preventViewNotify){
51044         if(this.locked) return;
51045         for(var i = startRow; i <= endRow; i++){
51046             this.deselectRow(i, preventViewNotify);
51047         }
51048     },
51049
51050     /**
51051      * Selects a row.
51052      * @param {Number} row The index of the row to select
51053      * @param {Boolean} keepExisting (optional) True to keep existing selections
51054      */
51055     selectRow : function(index, keepExisting, preventViewNotify){
51056         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
51057         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
51058             if(!keepExisting || this.singleSelect){
51059                 this.clearSelections();
51060             }
51061             var r = this.grid.dataSource.getAt(index);
51062             this.selections.add(r);
51063             this.last = this.lastActive = index;
51064             if(!preventViewNotify){
51065                 this.grid.getView().onRowSelect(index);
51066             }
51067             this.fireEvent("rowselect", this, index, r);
51068             this.fireEvent("selectionchange", this);
51069         }
51070     },
51071
51072     /**
51073      * Deselects a row.
51074      * @param {Number} row The index of the row to deselect
51075      */
51076     deselectRow : function(index, preventViewNotify){
51077         if(this.locked) return;
51078         if(this.last == index){
51079             this.last = false;
51080         }
51081         if(this.lastActive == index){
51082             this.lastActive = false;
51083         }
51084         var r = this.grid.dataSource.getAt(index);
51085         this.selections.remove(r);
51086         if(!preventViewNotify){
51087             this.grid.getView().onRowDeselect(index);
51088         }
51089         this.fireEvent("rowdeselect", this, index);
51090         this.fireEvent("selectionchange", this);
51091     },
51092
51093     // private
51094     restoreLast : function(){
51095         if(this._last){
51096             this.last = this._last;
51097         }
51098     },
51099
51100     // private
51101     acceptsNav : function(row, col, cm){
51102         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51103     },
51104
51105     // private
51106     onEditorKey : function(field, e){
51107         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51108         if(k == e.TAB){
51109             e.stopEvent();
51110             ed.completeEdit();
51111             if(e.shiftKey){
51112                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51113             }else{
51114                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51115             }
51116         }else if(k == e.ENTER && !e.ctrlKey){
51117             e.stopEvent();
51118             ed.completeEdit();
51119             if(e.shiftKey){
51120                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51121             }else{
51122                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51123             }
51124         }else if(k == e.ESC){
51125             ed.cancelEdit();
51126         }
51127         if(newCell){
51128             g.startEditing(newCell[0], newCell[1]);
51129         }
51130     }
51131 });/*
51132  * Based on:
51133  * Ext JS Library 1.1.1
51134  * Copyright(c) 2006-2007, Ext JS, LLC.
51135  *
51136  * Originally Released Under LGPL - original licence link has changed is not relivant.
51137  *
51138  * Fork - LGPL
51139  * <script type="text/javascript">
51140  */
51141 /**
51142  * @class Roo.grid.CellSelectionModel
51143  * @extends Roo.grid.AbstractSelectionModel
51144  * This class provides the basic implementation for cell selection in a grid.
51145  * @constructor
51146  * @param {Object} config The object containing the configuration of this model.
51147  */
51148 Roo.grid.CellSelectionModel = function(config){
51149     Roo.apply(this, config);
51150
51151     this.selection = null;
51152
51153     this.addEvents({
51154         /**
51155              * @event beforerowselect
51156              * Fires before a cell is selected.
51157              * @param {SelectionModel} this
51158              * @param {Number} rowIndex The selected row index
51159              * @param {Number} colIndex The selected cell index
51160              */
51161             "beforecellselect" : true,
51162         /**
51163              * @event cellselect
51164              * Fires when a cell is selected.
51165              * @param {SelectionModel} this
51166              * @param {Number} rowIndex The selected row index
51167              * @param {Number} colIndex The selected cell index
51168              */
51169             "cellselect" : true,
51170         /**
51171              * @event selectionchange
51172              * Fires when the active selection changes.
51173              * @param {SelectionModel} this
51174              * @param {Object} selection null for no selection or an object (o) with two properties
51175                 <ul>
51176                 <li>o.record: the record object for the row the selection is in</li>
51177                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51178                 </ul>
51179              */
51180             "selectionchange" : true
51181     });
51182     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51183 };
51184
51185 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51186
51187     /** @ignore */
51188     initEvents : function(){
51189         this.grid.on("mousedown", this.handleMouseDown, this);
51190         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51191         var view = this.grid.view;
51192         view.on("refresh", this.onViewChange, this);
51193         view.on("rowupdated", this.onRowUpdated, this);
51194         view.on("beforerowremoved", this.clearSelections, this);
51195         view.on("beforerowsinserted", this.clearSelections, this);
51196         if(this.grid.isEditor){
51197             this.grid.on("beforeedit", this.beforeEdit,  this);
51198         }
51199     },
51200
51201         //private
51202     beforeEdit : function(e){
51203         this.select(e.row, e.column, false, true, e.record);
51204     },
51205
51206         //private
51207     onRowUpdated : function(v, index, r){
51208         if(this.selection && this.selection.record == r){
51209             v.onCellSelect(index, this.selection.cell[1]);
51210         }
51211     },
51212
51213         //private
51214     onViewChange : function(){
51215         this.clearSelections(true);
51216     },
51217
51218         /**
51219          * Returns the currently selected cell,.
51220          * @return {Array} The selected cell (row, column) or null if none selected.
51221          */
51222     getSelectedCell : function(){
51223         return this.selection ? this.selection.cell : null;
51224     },
51225
51226     /**
51227      * Clears all selections.
51228      * @param {Boolean} true to prevent the gridview from being notified about the change.
51229      */
51230     clearSelections : function(preventNotify){
51231         var s = this.selection;
51232         if(s){
51233             if(preventNotify !== true){
51234                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51235             }
51236             this.selection = null;
51237             this.fireEvent("selectionchange", this, null);
51238         }
51239     },
51240
51241     /**
51242      * Returns true if there is a selection.
51243      * @return {Boolean}
51244      */
51245     hasSelection : function(){
51246         return this.selection ? true : false;
51247     },
51248
51249     /** @ignore */
51250     handleMouseDown : function(e, t){
51251         var v = this.grid.getView();
51252         if(this.isLocked()){
51253             return;
51254         };
51255         var row = v.findRowIndex(t);
51256         var cell = v.findCellIndex(t);
51257         if(row !== false && cell !== false){
51258             this.select(row, cell);
51259         }
51260     },
51261
51262     /**
51263      * Selects a cell.
51264      * @param {Number} rowIndex
51265      * @param {Number} collIndex
51266      */
51267     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
51268         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
51269             this.clearSelections();
51270             r = r || this.grid.dataSource.getAt(rowIndex);
51271             this.selection = {
51272                 record : r,
51273                 cell : [rowIndex, colIndex]
51274             };
51275             if(!preventViewNotify){
51276                 var v = this.grid.getView();
51277                 v.onCellSelect(rowIndex, colIndex);
51278                 if(preventFocus !== true){
51279                     v.focusCell(rowIndex, colIndex);
51280                 }
51281             }
51282             this.fireEvent("cellselect", this, rowIndex, colIndex);
51283             this.fireEvent("selectionchange", this, this.selection);
51284         }
51285     },
51286
51287         //private
51288     isSelectable : function(rowIndex, colIndex, cm){
51289         return !cm.isHidden(colIndex);
51290     },
51291
51292     /** @ignore */
51293     handleKeyDown : function(e){
51294         //Roo.log('Cell Sel Model handleKeyDown');
51295         if(!e.isNavKeyPress()){
51296             return;
51297         }
51298         var g = this.grid, s = this.selection;
51299         if(!s){
51300             e.stopEvent();
51301             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
51302             if(cell){
51303                 this.select(cell[0], cell[1]);
51304             }
51305             return;
51306         }
51307         var sm = this;
51308         var walk = function(row, col, step){
51309             return g.walkCells(row, col, step, sm.isSelectable,  sm);
51310         };
51311         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
51312         var newCell;
51313
51314         switch(k){
51315             case e.TAB:
51316                 // handled by onEditorKey
51317                 if (g.isEditor && g.editing) {
51318                     return;
51319                 }
51320                 if(e.shiftKey){
51321                      newCell = walk(r, c-1, -1);
51322                 }else{
51323                      newCell = walk(r, c+1, 1);
51324                 }
51325              break;
51326              case e.DOWN:
51327                  newCell = walk(r+1, c, 1);
51328              break;
51329              case e.UP:
51330                  newCell = walk(r-1, c, -1);
51331              break;
51332              case e.RIGHT:
51333                  newCell = walk(r, c+1, 1);
51334              break;
51335              case e.LEFT:
51336                  newCell = walk(r, c-1, -1);
51337              break;
51338              case e.ENTER:
51339                  if(g.isEditor && !g.editing){
51340                     g.startEditing(r, c);
51341                     e.stopEvent();
51342                     return;
51343                 }
51344              break;
51345         };
51346         if(newCell){
51347             this.select(newCell[0], newCell[1]);
51348             e.stopEvent();
51349         }
51350     },
51351
51352     acceptsNav : function(row, col, cm){
51353         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51354     },
51355     /**
51356      * Selects a cell.
51357      * @param {Number} field (not used) - as it's normally used as a listener
51358      * @param {Number} e - event - fake it by using
51359      *
51360      * var e = Roo.EventObjectImpl.prototype;
51361      * e.keyCode = e.TAB
51362      *
51363      * 
51364      */
51365     onEditorKey : function(field, e){
51366         
51367         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51368         ///Roo.log('onEditorKey' + k);
51369         if (!ed) {
51370             
51371             
51372             
51373         }
51374         if(k == e.TAB){
51375             if(e.shiftKey){
51376                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51377             }else{
51378                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51379             }
51380             
51381             e.stopEvent();
51382             
51383         }else if(k == e.ENTER &&  !e.ctrlKey){
51384             ed.completeEdit();
51385             e.stopEvent();
51386             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51387         }else if(k == e.ESC){
51388             ed.cancelEdit();
51389         }
51390         
51391         
51392         if(newCell){
51393             //Roo.log('next cell after edit');
51394             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
51395         }
51396     }
51397 });/*
51398  * Based on:
51399  * Ext JS Library 1.1.1
51400  * Copyright(c) 2006-2007, Ext JS, LLC.
51401  *
51402  * Originally Released Under LGPL - original licence link has changed is not relivant.
51403  *
51404  * Fork - LGPL
51405  * <script type="text/javascript">
51406  */
51407  
51408 /**
51409  * @class Roo.grid.EditorGrid
51410  * @extends Roo.grid.Grid
51411  * Class for creating and editable grid.
51412  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51413  * The container MUST have some type of size defined for the grid to fill. The container will be 
51414  * automatically set to position relative if it isn't already.
51415  * @param {Object} dataSource The data model to bind to
51416  * @param {Object} colModel The column model with info about this grid's columns
51417  */
51418 Roo.grid.EditorGrid = function(container, config){
51419     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51420     this.getGridEl().addClass("xedit-grid");
51421
51422     if(!this.selModel){
51423         this.selModel = new Roo.grid.CellSelectionModel();
51424     }
51425
51426     this.activeEditor = null;
51427
51428         this.addEvents({
51429             /**
51430              * @event beforeedit
51431              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51432              * <ul style="padding:5px;padding-left:16px;">
51433              * <li>grid - This grid</li>
51434              * <li>record - The record being edited</li>
51435              * <li>field - The field name being edited</li>
51436              * <li>value - The value for the field being edited.</li>
51437              * <li>row - The grid row index</li>
51438              * <li>column - The grid column index</li>
51439              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51440              * </ul>
51441              * @param {Object} e An edit event (see above for description)
51442              */
51443             "beforeedit" : true,
51444             /**
51445              * @event afteredit
51446              * Fires after a cell is edited. <br />
51447              * <ul style="padding:5px;padding-left:16px;">
51448              * <li>grid - This grid</li>
51449              * <li>record - The record being edited</li>
51450              * <li>field - The field name being edited</li>
51451              * <li>value - The value being set</li>
51452              * <li>originalValue - The original value for the field, before the edit.</li>
51453              * <li>row - The grid row index</li>
51454              * <li>column - The grid column index</li>
51455              * </ul>
51456              * @param {Object} e An edit event (see above for description)
51457              */
51458             "afteredit" : true,
51459             /**
51460              * @event validateedit
51461              * Fires after a cell is edited, but before the value is set in the record. 
51462          * You can use this to modify the value being set in the field, Return false
51463              * to cancel the change. The edit event object has the following properties <br />
51464              * <ul style="padding:5px;padding-left:16px;">
51465          * <li>editor - This editor</li>
51466              * <li>grid - This grid</li>
51467              * <li>record - The record being edited</li>
51468              * <li>field - The field name being edited</li>
51469              * <li>value - The value being set</li>
51470              * <li>originalValue - The original value for the field, before the edit.</li>
51471              * <li>row - The grid row index</li>
51472              * <li>column - The grid column index</li>
51473              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51474              * </ul>
51475              * @param {Object} e An edit event (see above for description)
51476              */
51477             "validateedit" : true
51478         });
51479     this.on("bodyscroll", this.stopEditing,  this);
51480     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51481 };
51482
51483 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51484     /**
51485      * @cfg {Number} clicksToEdit
51486      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51487      */
51488     clicksToEdit: 2,
51489
51490     // private
51491     isEditor : true,
51492     // private
51493     trackMouseOver: false, // causes very odd FF errors
51494
51495     onCellDblClick : function(g, row, col){
51496         this.startEditing(row, col);
51497     },
51498
51499     onEditComplete : function(ed, value, startValue){
51500         this.editing = false;
51501         this.activeEditor = null;
51502         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51503         var r = ed.record;
51504         var field = this.colModel.getDataIndex(ed.col);
51505         var e = {
51506             grid: this,
51507             record: r,
51508             field: field,
51509             originalValue: startValue,
51510             value: value,
51511             row: ed.row,
51512             column: ed.col,
51513             cancel:false,
51514             editor: ed
51515         };
51516         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
51517         cell.show();
51518           
51519         if(String(value) !== String(startValue)){
51520             
51521             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51522                 r.set(field, e.value);
51523                 // if we are dealing with a combo box..
51524                 // then we also set the 'name' colum to be the displayField
51525                 if (ed.field.displayField && ed.field.name) {
51526                     r.set(ed.field.name, ed.field.el.dom.value);
51527                 }
51528                 
51529                 delete e.cancel; //?? why!!!
51530                 this.fireEvent("afteredit", e);
51531             }
51532         } else {
51533             this.fireEvent("afteredit", e); // always fire it!
51534         }
51535         this.view.focusCell(ed.row, ed.col);
51536     },
51537
51538     /**
51539      * Starts editing the specified for the specified row/column
51540      * @param {Number} rowIndex
51541      * @param {Number} colIndex
51542      */
51543     startEditing : function(row, col){
51544         this.stopEditing();
51545         if(this.colModel.isCellEditable(col, row)){
51546             this.view.ensureVisible(row, col, true);
51547           
51548             var r = this.dataSource.getAt(row);
51549             var field = this.colModel.getDataIndex(col);
51550             var cell = Roo.get(this.view.getCell(row,col));
51551             var e = {
51552                 grid: this,
51553                 record: r,
51554                 field: field,
51555                 value: r.data[field],
51556                 row: row,
51557                 column: col,
51558                 cancel:false 
51559             };
51560             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51561                 this.editing = true;
51562                 var ed = this.colModel.getCellEditor(col, row);
51563                 
51564                 if (!ed) {
51565                     return;
51566                 }
51567                 if(!ed.rendered){
51568                     ed.render(ed.parentEl || document.body);
51569                 }
51570                 ed.field.reset();
51571                
51572                 cell.hide();
51573                 
51574                 (function(){ // complex but required for focus issues in safari, ie and opera
51575                     ed.row = row;
51576                     ed.col = col;
51577                     ed.record = r;
51578                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
51579                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
51580                     this.activeEditor = ed;
51581                     var v = r.data[field];
51582                     ed.startEdit(this.view.getCell(row, col), v);
51583                     // combo's with 'displayField and name set
51584                     if (ed.field.displayField && ed.field.name) {
51585                         ed.field.el.dom.value = r.data[ed.field.name];
51586                     }
51587                     
51588                     
51589                 }).defer(50, this);
51590             }
51591         }
51592     },
51593         
51594     /**
51595      * Stops any active editing
51596      */
51597     stopEditing : function(){
51598         if(this.activeEditor){
51599             this.activeEditor.completeEdit();
51600         }
51601         this.activeEditor = null;
51602     }
51603 });/*
51604  * Based on:
51605  * Ext JS Library 1.1.1
51606  * Copyright(c) 2006-2007, Ext JS, LLC.
51607  *
51608  * Originally Released Under LGPL - original licence link has changed is not relivant.
51609  *
51610  * Fork - LGPL
51611  * <script type="text/javascript">
51612  */
51613
51614 // private - not really -- you end up using it !
51615 // This is a support class used internally by the Grid components
51616
51617 /**
51618  * @class Roo.grid.GridEditor
51619  * @extends Roo.Editor
51620  * Class for creating and editable grid elements.
51621  * @param {Object} config any settings (must include field)
51622  */
51623 Roo.grid.GridEditor = function(field, config){
51624     if (!config && field.field) {
51625         config = field;
51626         field = Roo.factory(config.field, Roo.form);
51627     }
51628     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51629     field.monitorTab = false;
51630 };
51631
51632 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51633     
51634     /**
51635      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51636      */
51637     
51638     alignment: "tl-tl",
51639     autoSize: "width",
51640     hideEl : false,
51641     cls: "x-small-editor x-grid-editor",
51642     shim:false,
51643     shadow:"frame"
51644 });/*
51645  * Based on:
51646  * Ext JS Library 1.1.1
51647  * Copyright(c) 2006-2007, Ext JS, LLC.
51648  *
51649  * Originally Released Under LGPL - original licence link has changed is not relivant.
51650  *
51651  * Fork - LGPL
51652  * <script type="text/javascript">
51653  */
51654   
51655
51656   
51657 Roo.grid.PropertyRecord = Roo.data.Record.create([
51658     {name:'name',type:'string'},  'value'
51659 ]);
51660
51661
51662 Roo.grid.PropertyStore = function(grid, source){
51663     this.grid = grid;
51664     this.store = new Roo.data.Store({
51665         recordType : Roo.grid.PropertyRecord
51666     });
51667     this.store.on('update', this.onUpdate,  this);
51668     if(source){
51669         this.setSource(source);
51670     }
51671     Roo.grid.PropertyStore.superclass.constructor.call(this);
51672 };
51673
51674
51675
51676 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
51677     setSource : function(o){
51678         this.source = o;
51679         this.store.removeAll();
51680         var data = [];
51681         for(var k in o){
51682             if(this.isEditableValue(o[k])){
51683                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
51684             }
51685         }
51686         this.store.loadRecords({records: data}, {}, true);
51687     },
51688
51689     onUpdate : function(ds, record, type){
51690         if(type == Roo.data.Record.EDIT){
51691             var v = record.data['value'];
51692             var oldValue = record.modified['value'];
51693             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
51694                 this.source[record.id] = v;
51695                 record.commit();
51696                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
51697             }else{
51698                 record.reject();
51699             }
51700         }
51701     },
51702
51703     getProperty : function(row){
51704        return this.store.getAt(row);
51705     },
51706
51707     isEditableValue: function(val){
51708         if(val && val instanceof Date){
51709             return true;
51710         }else if(typeof val == 'object' || typeof val == 'function'){
51711             return false;
51712         }
51713         return true;
51714     },
51715
51716     setValue : function(prop, value){
51717         this.source[prop] = value;
51718         this.store.getById(prop).set('value', value);
51719     },
51720
51721     getSource : function(){
51722         return this.source;
51723     }
51724 });
51725
51726 Roo.grid.PropertyColumnModel = function(grid, store){
51727     this.grid = grid;
51728     var g = Roo.grid;
51729     g.PropertyColumnModel.superclass.constructor.call(this, [
51730         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
51731         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
51732     ]);
51733     this.store = store;
51734     this.bselect = Roo.DomHelper.append(document.body, {
51735         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
51736             {tag: 'option', value: 'true', html: 'true'},
51737             {tag: 'option', value: 'false', html: 'false'}
51738         ]
51739     });
51740     Roo.id(this.bselect);
51741     var f = Roo.form;
51742     this.editors = {
51743         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
51744         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
51745         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
51746         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
51747         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
51748     };
51749     this.renderCellDelegate = this.renderCell.createDelegate(this);
51750     this.renderPropDelegate = this.renderProp.createDelegate(this);
51751 };
51752
51753 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
51754     
51755     
51756     nameText : 'Name',
51757     valueText : 'Value',
51758     
51759     dateFormat : 'm/j/Y',
51760     
51761     
51762     renderDate : function(dateVal){
51763         return dateVal.dateFormat(this.dateFormat);
51764     },
51765
51766     renderBool : function(bVal){
51767         return bVal ? 'true' : 'false';
51768     },
51769
51770     isCellEditable : function(colIndex, rowIndex){
51771         return colIndex == 1;
51772     },
51773
51774     getRenderer : function(col){
51775         return col == 1 ?
51776             this.renderCellDelegate : this.renderPropDelegate;
51777     },
51778
51779     renderProp : function(v){
51780         return this.getPropertyName(v);
51781     },
51782
51783     renderCell : function(val){
51784         var rv = val;
51785         if(val instanceof Date){
51786             rv = this.renderDate(val);
51787         }else if(typeof val == 'boolean'){
51788             rv = this.renderBool(val);
51789         }
51790         return Roo.util.Format.htmlEncode(rv);
51791     },
51792
51793     getPropertyName : function(name){
51794         var pn = this.grid.propertyNames;
51795         return pn && pn[name] ? pn[name] : name;
51796     },
51797
51798     getCellEditor : function(colIndex, rowIndex){
51799         var p = this.store.getProperty(rowIndex);
51800         var n = p.data['name'], val = p.data['value'];
51801         
51802         if(typeof(this.grid.customEditors[n]) == 'string'){
51803             return this.editors[this.grid.customEditors[n]];
51804         }
51805         if(typeof(this.grid.customEditors[n]) != 'undefined'){
51806             return this.grid.customEditors[n];
51807         }
51808         if(val instanceof Date){
51809             return this.editors['date'];
51810         }else if(typeof val == 'number'){
51811             return this.editors['number'];
51812         }else if(typeof val == 'boolean'){
51813             return this.editors['boolean'];
51814         }else{
51815             return this.editors['string'];
51816         }
51817     }
51818 });
51819
51820 /**
51821  * @class Roo.grid.PropertyGrid
51822  * @extends Roo.grid.EditorGrid
51823  * This class represents the  interface of a component based property grid control.
51824  * <br><br>Usage:<pre><code>
51825  var grid = new Roo.grid.PropertyGrid("my-container-id", {
51826       
51827  });
51828  // set any options
51829  grid.render();
51830  * </code></pre>
51831   
51832  * @constructor
51833  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51834  * The container MUST have some type of size defined for the grid to fill. The container will be
51835  * automatically set to position relative if it isn't already.
51836  * @param {Object} config A config object that sets properties on this grid.
51837  */
51838 Roo.grid.PropertyGrid = function(container, config){
51839     config = config || {};
51840     var store = new Roo.grid.PropertyStore(this);
51841     this.store = store;
51842     var cm = new Roo.grid.PropertyColumnModel(this, store);
51843     store.store.sort('name', 'ASC');
51844     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
51845         ds: store.store,
51846         cm: cm,
51847         enableColLock:false,
51848         enableColumnMove:false,
51849         stripeRows:false,
51850         trackMouseOver: false,
51851         clicksToEdit:1
51852     }, config));
51853     this.getGridEl().addClass('x-props-grid');
51854     this.lastEditRow = null;
51855     this.on('columnresize', this.onColumnResize, this);
51856     this.addEvents({
51857          /**
51858              * @event beforepropertychange
51859              * Fires before a property changes (return false to stop?)
51860              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51861              * @param {String} id Record Id
51862              * @param {String} newval New Value
51863          * @param {String} oldval Old Value
51864              */
51865         "beforepropertychange": true,
51866         /**
51867              * @event propertychange
51868              * Fires after a property changes
51869              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51870              * @param {String} id Record Id
51871              * @param {String} newval New Value
51872          * @param {String} oldval Old Value
51873              */
51874         "propertychange": true
51875     });
51876     this.customEditors = this.customEditors || {};
51877 };
51878 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
51879     
51880      /**
51881      * @cfg {Object} customEditors map of colnames=> custom editors.
51882      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
51883      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
51884      * false disables editing of the field.
51885          */
51886     
51887       /**
51888      * @cfg {Object} propertyNames map of property Names to their displayed value
51889          */
51890     
51891     render : function(){
51892         Roo.grid.PropertyGrid.superclass.render.call(this);
51893         this.autoSize.defer(100, this);
51894     },
51895
51896     autoSize : function(){
51897         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
51898         if(this.view){
51899             this.view.fitColumns();
51900         }
51901     },
51902
51903     onColumnResize : function(){
51904         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
51905         this.autoSize();
51906     },
51907     /**
51908      * Sets the data for the Grid
51909      * accepts a Key => Value object of all the elements avaiable.
51910      * @param {Object} data  to appear in grid.
51911      */
51912     setSource : function(source){
51913         this.store.setSource(source);
51914         //this.autoSize();
51915     },
51916     /**
51917      * Gets all the data from the grid.
51918      * @return {Object} data  data stored in grid
51919      */
51920     getSource : function(){
51921         return this.store.getSource();
51922     }
51923 });/*
51924  * Based on:
51925  * Ext JS Library 1.1.1
51926  * Copyright(c) 2006-2007, Ext JS, LLC.
51927  *
51928  * Originally Released Under LGPL - original licence link has changed is not relivant.
51929  *
51930  * Fork - LGPL
51931  * <script type="text/javascript">
51932  */
51933  
51934 /**
51935  * @class Roo.LoadMask
51936  * A simple utility class for generically masking elements while loading data.  If the element being masked has
51937  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
51938  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
51939  * element's UpdateManager load indicator and will be destroyed after the initial load.
51940  * @constructor
51941  * Create a new LoadMask
51942  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
51943  * @param {Object} config The config object
51944  */
51945 Roo.LoadMask = function(el, config){
51946     this.el = Roo.get(el);
51947     Roo.apply(this, config);
51948     if(this.store){
51949         this.store.on('beforeload', this.onBeforeLoad, this);
51950         this.store.on('load', this.onLoad, this);
51951         this.store.on('loadexception', this.onLoadException, this);
51952         this.removeMask = false;
51953     }else{
51954         var um = this.el.getUpdateManager();
51955         um.showLoadIndicator = false; // disable the default indicator
51956         um.on('beforeupdate', this.onBeforeLoad, this);
51957         um.on('update', this.onLoad, this);
51958         um.on('failure', this.onLoad, this);
51959         this.removeMask = true;
51960     }
51961 };
51962
51963 Roo.LoadMask.prototype = {
51964     /**
51965      * @cfg {Boolean} removeMask
51966      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
51967      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
51968      */
51969     /**
51970      * @cfg {String} msg
51971      * The text to display in a centered loading message box (defaults to 'Loading...')
51972      */
51973     msg : 'Loading...',
51974     /**
51975      * @cfg {String} msgCls
51976      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
51977      */
51978     msgCls : 'x-mask-loading',
51979
51980     /**
51981      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
51982      * @type Boolean
51983      */
51984     disabled: false,
51985
51986     /**
51987      * Disables the mask to prevent it from being displayed
51988      */
51989     disable : function(){
51990        this.disabled = true;
51991     },
51992
51993     /**
51994      * Enables the mask so that it can be displayed
51995      */
51996     enable : function(){
51997         this.disabled = false;
51998     },
51999     
52000     onLoadException : function()
52001     {
52002         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52003             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52004         }
52005         this.el.unmask(this.removeMask);
52006     },
52007     // private
52008     onLoad : function()
52009     {
52010         this.el.unmask(this.removeMask);
52011     },
52012
52013     // private
52014     onBeforeLoad : function(){
52015         if(!this.disabled){
52016             this.el.mask(this.msg, this.msgCls);
52017         }
52018     },
52019
52020     // private
52021     destroy : function(){
52022         if(this.store){
52023             this.store.un('beforeload', this.onBeforeLoad, this);
52024             this.store.un('load', this.onLoad, this);
52025             this.store.un('loadexception', this.onLoadException, this);
52026         }else{
52027             var um = this.el.getUpdateManager();
52028             um.un('beforeupdate', this.onBeforeLoad, this);
52029             um.un('update', this.onLoad, this);
52030             um.un('failure', this.onLoad, this);
52031         }
52032     }
52033 };/*
52034  * Based on:
52035  * Ext JS Library 1.1.1
52036  * Copyright(c) 2006-2007, Ext JS, LLC.
52037  *
52038  * Originally Released Under LGPL - original licence link has changed is not relivant.
52039  *
52040  * Fork - LGPL
52041  * <script type="text/javascript">
52042  */
52043 Roo.XTemplate = function(){
52044     Roo.XTemplate.superclass.constructor.apply(this, arguments);
52045     var s = this.html;
52046
52047     s = ['<tpl>', s, '</tpl>'].join('');
52048
52049     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
52050
52051     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
52052     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
52053     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
52054     var m, id = 0;
52055     var tpls = [];
52056
52057     while(m = s.match(re)){
52058        var m2 = m[0].match(nameRe);
52059        var m3 = m[0].match(ifRe);
52060        var m4 = m[0].match(execRe);
52061        var exp = null, fn = null, exec = null;
52062        var name = m2 && m2[1] ? m2[1] : '';
52063        if(m3){
52064            exp = m3 && m3[1] ? m3[1] : null;
52065            if(exp){
52066                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
52067            }
52068        }
52069        if(m4){
52070            exp = m4 && m4[1] ? m4[1] : null;
52071            if(exp){
52072                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52073            }
52074        }
52075        if(name){
52076            switch(name){
52077                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52078                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52079                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52080            }
52081        }
52082        tpls.push({
52083             id: id,
52084             target: name,
52085             exec: exec,
52086             test: fn,
52087             body: m[1]||''
52088         });
52089        s = s.replace(m[0], '{xtpl'+ id + '}');
52090        ++id;
52091     }
52092     for(var i = tpls.length-1; i >= 0; --i){
52093         this.compileTpl(tpls[i]);
52094     }
52095     this.master = tpls[tpls.length-1];
52096     this.tpls = tpls;
52097 };
52098 Roo.extend(Roo.XTemplate, Roo.Template, {
52099
52100     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52101
52102     applySubTemplate : function(id, values, parent){
52103         var t = this.tpls[id];
52104         if(t.test && !t.test.call(this, values, parent)){
52105             return '';
52106         }
52107         if(t.exec && t.exec.call(this, values, parent)){
52108             return '';
52109         }
52110         var vs = t.target ? t.target.call(this, values, parent) : values;
52111         parent = t.target ? values : parent;
52112         if(t.target && vs instanceof Array){
52113             var buf = [];
52114             for(var i = 0, len = vs.length; i < len; i++){
52115                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
52116             }
52117             return buf.join('');
52118         }
52119         return t.compiled.call(this, vs, parent);
52120     },
52121
52122     compileTpl : function(tpl){
52123         var fm = Roo.util.Format;
52124         var useF = this.disableFormats !== true;
52125         var sep = Roo.isGecko ? "+" : ",";
52126         var fn = function(m, name, format, args){
52127             if(name.substr(0, 4) == 'xtpl'){
52128                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
52129             }
52130             var v;
52131             if(name.indexOf('.') != -1){
52132                 v = name;
52133             }else{
52134                 v = "values['" + name + "']";
52135             }
52136             if(format && useF){
52137                 args = args ? ',' + args : "";
52138                 if(format.substr(0, 5) != "this."){
52139                     format = "fm." + format + '(';
52140                 }else{
52141                     format = 'this.call("'+ format.substr(5) + '", ';
52142                     args = ", values";
52143                 }
52144             }else{
52145                 args= ''; format = "("+v+" === undefined ? '' : ";
52146             }
52147             return "'"+ sep + format + v + args + ")"+sep+"'";
52148         };
52149         var body;
52150         // branched to use + in gecko and [].join() in others
52151         if(Roo.isGecko){
52152             body = "tpl.compiled = function(values, parent){ return '" +
52153                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
52154                     "';};";
52155         }else{
52156             body = ["tpl.compiled = function(values, parent){ return ['"];
52157             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
52158             body.push("'].join('');};");
52159             body = body.join('');
52160         }
52161         /** eval:var:zzzzzzz */
52162         eval(body);
52163         return this;
52164     },
52165
52166     applyTemplate : function(values){
52167         return this.master.compiled.call(this, values, {});
52168         var s = this.subs;
52169     },
52170
52171     apply : function(){
52172         return this.applyTemplate.apply(this, arguments);
52173     },
52174
52175     compile : function(){return this;}
52176 });
52177
52178 Roo.XTemplate.from = function(el){
52179     el = Roo.getDom(el);
52180     return new Roo.XTemplate(el.value || el.innerHTML);
52181 };/*
52182  * Original code for Roojs - LGPL
52183  * <script type="text/javascript">
52184  */
52185  
52186 /**
52187  * @class Roo.XComponent
52188  * A delayed Element creator...
52189  * Or a way to group chunks of interface together.
52190  * 
52191  * Mypart.xyx = new Roo.XComponent({
52192
52193     parent : 'Mypart.xyz', // empty == document.element.!!
52194     order : '001',
52195     name : 'xxxx'
52196     region : 'xxxx'
52197     disabled : function() {} 
52198      
52199     tree : function() { // return an tree of xtype declared components
52200         var MODULE = this;
52201         return 
52202         {
52203             xtype : 'NestedLayoutPanel',
52204             // technicall
52205         }
52206      ]
52207  *})
52208  *
52209  *
52210  * It can be used to build a big heiracy, with parent etc.
52211  * or you can just use this to render a single compoent to a dom element
52212  * MYPART.render(Roo.Element | String(id) | dom_element )
52213  * 
52214  * @extends Roo.util.Observable
52215  * @constructor
52216  * @param cfg {Object} configuration of component
52217  * 
52218  */
52219 Roo.XComponent = function(cfg) {
52220     Roo.apply(this, cfg);
52221     this.addEvents({ 
52222         /**
52223              * @event built
52224              * Fires when this the componnt is built
52225              * @param {Roo.XComponent} c the component
52226              */
52227         'built' : true,
52228         /**
52229              * @event buildcomplete
52230              * Fires on the top level element when all elements have been built
52231              * @param {Roo.XComponent} c the top level component.
52232          */
52233         'buildcomplete' : true
52234         
52235     });
52236     this.region = this.region || 'center'; // default..
52237     Roo.XComponent.register(this);
52238     this.modules = false;
52239     this.el = false; // where the layout goes..
52240     
52241     
52242 }
52243 Roo.extend(Roo.XComponent, Roo.util.Observable, {
52244     /**
52245      * @property el
52246      * The created element (with Roo.factory())
52247      * @type {Roo.Layout}
52248      */
52249     el  : false,
52250     
52251     /**
52252      * @property el
52253      * for BC  - use el in new code
52254      * @type {Roo.Layout}
52255      */
52256     panel : false,
52257     
52258     /**
52259      * @property layout
52260      * for BC  - use el in new code
52261      * @type {Roo.Layout}
52262      */
52263     layout : false,
52264     
52265      /**
52266      * @cfg {Function|boolean} disabled
52267      * If this module is disabled by some rule, return true from the funtion
52268      */
52269     disabled : false,
52270     
52271     /**
52272      * @cfg {String} parent 
52273      * Name of parent element which it get xtype added to..
52274      */
52275     parent: false,
52276     
52277     /**
52278      * @cfg {String} order
52279      * Used to set the order in which elements are created (usefull for multiple tabs)
52280      */
52281     
52282     order : false,
52283     /**
52284      * @cfg {String} name
52285      * String to display while loading.
52286      */
52287     name : false,
52288     /**
52289      * @cfg {String} region
52290      * Region to render component to (defaults to center)
52291      */
52292     region : 'center',
52293     
52294     /**
52295      * @cfg {Array} items
52296      * A single item array - the first element is the root of the tree..
52297      * It's done this way to stay compatible with the Xtype system...
52298      */
52299     items : false,
52300     
52301     
52302      /**
52303      * render
52304      * render element to dom or tree
52305      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
52306      */
52307     
52308     render : function(el)
52309     {
52310         
52311         el = el || false;
52312         var hp = this.parent ? 1 : 0;
52313         
52314         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
52315             // if parent is a '#.....' string, then let's use that..
52316             var ename = this.parent.substr(1)
52317             this.parent = false;
52318             el = Roo.get(ename);
52319             if (!el) {
52320                 Roo.log("Warning - element can not be found :#" + ename );
52321                 return;
52322             }
52323         }
52324         
52325         
52326         if (!this.parent) {
52327             
52328             el = el ? Roo.get(el) : false;
52329             
52330             // it's a top level one..
52331             this.parent =  {
52332                 el : new Roo.BorderLayout(el || document.body, {
52333                 
52334                      center: {
52335                          titlebar: false,
52336                          autoScroll:false,
52337                          closeOnTab: true,
52338                          tabPosition: 'top',
52339                           //resizeTabs: true,
52340                          alwaysShowTabs: el && hp? false :  true,
52341                          hideTabs: el || !hp ? true :  false,
52342                          minTabWidth: 140
52343                      }
52344                  })
52345             }
52346         }
52347         
52348         
52349             
52350         var tree = this.tree();
52351         tree.region = tree.region || this.region;
52352         this.el = this.parent.el.addxtype(tree);
52353         this.fireEvent('built', this);
52354         
52355         this.panel = this.el;
52356         this.layout = this.panel.layout;    
52357          
52358     }
52359     
52360 });
52361
52362 Roo.apply(Roo.XComponent, {
52363     
52364     /**
52365      * @property  buildCompleted
52366      * True when the builder has completed building the interface.
52367      * @type Boolean
52368      */
52369     buildCompleted : false,
52370      
52371     /**
52372      * @property  topModule
52373      * the upper most module - uses document.element as it's constructor.
52374      * @type Object
52375      */
52376      
52377     topModule  : false,
52378       
52379     /**
52380      * @property  modules
52381      * array of modules to be created by registration system.
52382      * @type {Array} of Roo.XComponent
52383      */
52384     
52385     modules : [],
52386     /**
52387      * @property  elmodules
52388      * array of modules to be created by which use #ID 
52389      * @type {Array} of Roo.XComponent
52390      */
52391      
52392     elmodules : [],
52393
52394     
52395     /**
52396      * Register components to be built later.
52397      *
52398      * This solves the following issues
52399      * - Building is not done on page load, but after an authentication process has occured.
52400      * - Interface elements are registered on page load
52401      * - Parent Interface elements may not be loaded before child, so this handles that..
52402      * 
52403      *
52404      * example:
52405      * 
52406      * MyApp.register({
52407           order : '000001',
52408           module : 'Pman.Tab.projectMgr',
52409           region : 'center',
52410           parent : 'Pman.layout',
52411           disabled : false,  // or use a function..
52412         })
52413      
52414      * * @param {Object} details about module
52415      */
52416     register : function(obj) {
52417         this.modules.push(obj);
52418          
52419     },
52420     /**
52421      * convert a string to an object..
52422      * eg. 'AAA.BBB' -> finds AAA.BBB
52423
52424      */
52425     
52426     toObject : function(str)
52427     {
52428         if (!str || typeof(str) == 'object') {
52429             return str;
52430         }
52431         if (str.substring(0,1) == '#') {
52432             return str;
52433         }
52434
52435         var ar = str.split('.');
52436         var rt, o;
52437         rt = ar.shift();
52438             /** eval:var:o */
52439         try {
52440             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52441         } catch (e) {
52442             throw "Module not found : " + str;
52443         }
52444         
52445         if (o === false) {
52446             throw "Module not found : " + str;
52447         }
52448         Roo.each(ar, function(e) {
52449             if (typeof(o[e]) == 'undefined') {
52450                 throw "Module not found : " + str;
52451             }
52452             o = o[e];
52453         });
52454         
52455         return o;
52456         
52457     },
52458     
52459     
52460     /**
52461      * move modules into their correct place in the tree..
52462      * 
52463      */
52464     preBuild : function ()
52465     {
52466         var _t = this;
52467         Roo.each(this.modules , function (obj)
52468         {
52469             var opar = obj.parent;
52470             try { 
52471                 obj.parent = this.toObject(opar);
52472             } catch(e) {
52473                 Roo.log(e.toString());
52474                 return;
52475             }
52476             
52477             if (!obj.parent) {
52478                 this.topModule = obj;
52479                 return;
52480             }
52481             if (typeof(obj.parent) == 'string') {
52482                 this.elmodules.push(obj);
52483                 return;
52484             }
52485             if (obj.parent.constructor != Roo.XComponent) {
52486                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
52487             }
52488             if (!obj.parent.modules) {
52489                 obj.parent.modules = new Roo.util.MixedCollection(false, 
52490                     function(o) { return o.order + '' }
52491                 );
52492             }
52493             
52494             obj.parent.modules.add(obj);
52495         }, this);
52496     },
52497     
52498      /**
52499      * make a list of modules to build.
52500      * @return {Array} list of modules. 
52501      */ 
52502     
52503     buildOrder : function()
52504     {
52505         var _this = this;
52506         var cmp = function(a,b) {   
52507             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
52508         };
52509         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
52510             throw "No top level modules to build";
52511         }
52512         
52513         // make a flat list in order of modules to build.
52514         var mods = this.topModule ? [ this.topModule ] : [];
52515         Roo.each(this.elmodules,function(e) { mods.push(e) });
52516
52517         
52518         // add modules to their parents..
52519         var addMod = function(m) {
52520            // Roo.debug && Roo.log(m.modKey);
52521             
52522             mods.push(m);
52523             if (m.modules) {
52524                 m.modules.keySort('ASC',  cmp );
52525                 m.modules.each(addMod);
52526             }
52527             // not sure if this is used any more..
52528             if (m.finalize) {
52529                 m.finalize.name = m.name + " (clean up) ";
52530                 mods.push(m.finalize);
52531             }
52532             
52533         }
52534         if (this.topModule) { 
52535             this.topModule.modules.keySort('ASC',  cmp );
52536             this.topModule.modules.each(addMod);
52537         }
52538         return mods;
52539     },
52540     
52541      /**
52542      * Build the registered modules.
52543      * @param {Object} parent element.
52544      * @param {Function} optional method to call after module has been added.
52545      * 
52546      */ 
52547    
52548     build : function() 
52549     {
52550         
52551         this.preBuild();
52552         var mods = this.buildOrder();
52553       
52554         //this.allmods = mods;
52555         //Roo.debug && Roo.log(mods);
52556         //return;
52557         if (!mods.length) { // should not happen
52558             throw "NO modules!!!";
52559         }
52560         
52561         
52562         
52563         // flash it up as modal - so we store the mask!?
52564         Roo.MessageBox.show({ title: 'loading' });
52565         Roo.MessageBox.show({
52566            title: "Please wait...",
52567            msg: "Building Interface...",
52568            width:450,
52569            progress:true,
52570            closable:false,
52571            modal: false
52572           
52573         });
52574         var total = mods.length;
52575         
52576         var _this = this;
52577         var progressRun = function() {
52578             if (!mods.length) {
52579                 Roo.debug && Roo.log('hide?');
52580                 Roo.MessageBox.hide();
52581                 if (_this.topModule) { 
52582                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
52583                 }
52584                 // THE END...
52585                 return false;   
52586             }
52587             
52588             var m = mods.shift();
52589             
52590             
52591             Roo.debug && Roo.log(m);
52592             // not sure if this is supported any more.. - modules that are are just function
52593             if (typeof(m) == 'function') { 
52594                 m.call(this);
52595                 return progressRun.defer(10, _this);
52596             } 
52597             
52598             
52599             
52600             Roo.MessageBox.updateProgress(
52601                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
52602                     " of " + total + 
52603                     (m.name ? (' - ' + m.name) : '')
52604                     );
52605             
52606          
52607             // is the module disabled?
52608             var disabled = (typeof(m.disabled) == 'function') ?
52609                 m.disabled.call(m.module.disabled) : m.disabled;    
52610             
52611             
52612             if (disabled) {
52613                 return progressRun(); // we do not update the display!
52614             }
52615             
52616             // now build 
52617             
52618             m.render();
52619             // it's 10 on top level, and 1 on others??? why...
52620             return progressRun.defer(10, _this);
52621              
52622         }
52623         progressRun.defer(1, _this);
52624      
52625         
52626         
52627     }
52628     
52629      
52630    
52631     
52632     
52633 });
52634  //<script type="text/javascript">
52635
52636
52637 /**
52638  * @class Roo.Login
52639  * @extends Roo.LayoutDialog
52640  * A generic Login Dialog..... - only one needed in theory!?!?
52641  *
52642  * Fires XComponent builder on success...
52643  * 
52644  * Sends 
52645  *    username,password, lang = for login actions.
52646  *    check = 1 for periodic checking that sesion is valid.
52647  *    passwordRequest = email request password
52648  *    logout = 1 = to logout
52649  * 
52650  * Affects: (this id="????" elements)
52651  *   loading  (removed) (used to indicate application is loading)
52652  *   loading-mask (hides) (used to hide application when it's building loading)
52653  *   
52654  * 
52655  * Usage: 
52656  *    
52657  * 
52658  * Myapp.login = Roo.Login({
52659      url: xxxx,
52660    
52661      realm : 'Myapp', 
52662      
52663      
52664      method : 'POST',
52665      
52666      
52667      * 
52668  })
52669  * 
52670  * 
52671  * 
52672  **/
52673  
52674 Roo.Login = function(cfg)
52675 {
52676     this.addEvents({
52677         'refreshed' : true
52678     });
52679     
52680     Roo.apply(this,cfg);
52681     
52682     Roo.onReady(function() {
52683         this.onLoad();
52684     }, this);
52685     // call parent..
52686     
52687    
52688     Roo.Login.superclass.constructor.call(this, this);
52689     //this.addxtype(this.items[0]);
52690     
52691     
52692 }
52693
52694
52695 Roo.extend(Roo.Login, Roo.LayoutDialog, {
52696     
52697     /**
52698      * @cfg {String} method
52699      * Method used to query for login details.
52700      */
52701     
52702     method : 'POST',
52703     /**
52704      * @cfg {String} url
52705      * URL to query login data. - eg. baseURL + '/Login.php'
52706      */
52707     url : '',
52708     
52709     /**
52710      * @property user
52711      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
52712      * @type {Object} 
52713      */
52714     user : false,
52715     /**
52716      * @property checkFails
52717      * Number of times we have attempted to get authentication check, and failed.
52718      * @type {Number} 
52719      */
52720     checkFails : 0,
52721       /**
52722      * @property intervalID
52723      * The window interval that does the constant login checking.
52724      * @type {Number} 
52725      */
52726     intervalID : 0,
52727     
52728     
52729     onLoad : function() // called on page load...
52730     {
52731         // load 
52732          
52733         if (Roo.get('loading')) { // clear any loading indicator..
52734             Roo.get('loading').remove();
52735         }
52736         
52737         //this.switchLang('en'); // set the language to english..
52738        
52739         this.check({
52740             success:  function(response, opts)  {  // check successfull...
52741             
52742                 var res = this.processResponse(response);
52743                 this.checkFails =0;
52744                 if (!res.success) { // error!
52745                     this.checkFails = 5;
52746                     //console.log('call failure');
52747                     return this.failure(response,opts);
52748                 }
52749                 
52750                 if (!res.data.id) { // id=0 == login failure.
52751                     return this.show();
52752                 }
52753                 
52754                               
52755                         //console.log(success);
52756                 this.fillAuth(res.data);   
52757                 this.checkFails =0;
52758                 Roo.XComponent.build();
52759             },
52760             failure : this.show
52761         });
52762         
52763     }, 
52764     
52765     
52766     check: function(cfg) // called every so often to refresh cookie etc..
52767     {
52768         if (cfg.again) { // could be undefined..
52769             this.checkFails++;
52770         } else {
52771             this.checkFails = 0;
52772         }
52773         var _this = this;
52774         if (this.sending) {
52775             if ( this.checkFails > 4) {
52776                 Roo.MessageBox.alert("Error",  
52777                     "Error getting authentication status. - try reloading, or wait a while", function() {
52778                         _this.sending = false;
52779                     }); 
52780                 return;
52781             }
52782             cfg.again = true;
52783             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
52784             return;
52785         }
52786         this.sending = true;
52787         
52788         Roo.Ajax.request({  
52789             url: this.url,
52790             params: {
52791                 getAuthUser: true
52792             },  
52793             method: this.method,
52794             success:  cfg.success || this.success,
52795             failure : cfg.failure || this.failure,
52796             scope : this,
52797             callCfg : cfg
52798               
52799         });  
52800     }, 
52801     
52802     
52803     logout: function()
52804     {
52805         window.onbeforeunload = function() { }; // false does not work for IE..
52806         this.user = false;
52807         var _this = this;
52808         
52809         Roo.Ajax.request({  
52810             url: this.url,
52811             params: {
52812                 logout: 1
52813             },  
52814             method: 'GET',
52815             failure : function() {
52816                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
52817                     document.location = document.location.toString() + '?ts=' + Math.random();
52818                 });
52819                 
52820             },
52821             success : function() {
52822                 _this.user = false;
52823                 this.checkFails =0;
52824                 // fixme..
52825                 document.location = document.location.toString() + '?ts=' + Math.random();
52826             }
52827               
52828               
52829         }); 
52830     },
52831     
52832     processResponse : function (response)
52833     {
52834         var res = '';
52835         try {
52836             res = Roo.decode(response.responseText);
52837             // oops...
52838             if (typeof(res) != 'object') {
52839                 res = { success : false, errorMsg : res, errors : true };
52840             }
52841             if (typeof(res.success) == 'undefined') {
52842                 res.success = false;
52843             }
52844             
52845         } catch(e) {
52846             res = { success : false,  errorMsg : response.responseText, errors : true };
52847         }
52848         return res;
52849     },
52850     
52851     success : function(response, opts)  // check successfull...
52852     {  
52853         this.sending = false;
52854         var res = this.processResponse(response);
52855         if (!res.success) {
52856             return this.failure(response, opts);
52857         }
52858         if (!res.data || !res.data.id) {
52859             return this.failure(response,opts);
52860         }
52861         //console.log(res);
52862         this.fillAuth(res.data);
52863         
52864         this.checkFails =0;
52865         
52866     },
52867     
52868     
52869     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
52870     {
52871         this.authUser = -1;
52872         this.sending = false;
52873         var res = this.processResponse(response);
52874         //console.log(res);
52875         if ( this.checkFails > 2) {
52876         
52877             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
52878                 "Error getting authentication status. - try reloading"); 
52879             return;
52880         }
52881         opts.callCfg.again = true;
52882         this.check.defer(1000, this, [ opts.callCfg ]);
52883         return;  
52884     },
52885     
52886     
52887     
52888     fillAuth: function(au) {
52889         this.startAuthCheck();
52890         this.authUserId = au.id;
52891         this.authUser = au;
52892         this.lastChecked = new Date();
52893         this.fireEvent('refreshed', au);
52894         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
52895         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
52896         au.lang = au.lang || 'en';
52897         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
52898         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
52899         this.switchLang(au.lang );
52900         
52901      
52902         // open system... - -on setyp..
52903         if (this.authUserId  < 0) {
52904             Roo.MessageBox.alert("Warning", 
52905                 "This is an open system - please set up a admin user with a password.");  
52906         }
52907          
52908         //Pman.onload(); // which should do nothing if it's a re-auth result...
52909         
52910              
52911     },
52912     
52913     startAuthCheck : function() // starter for timeout checking..
52914     {
52915         if (this.intervalID) { // timer already in place...
52916             return false;
52917         }
52918         var _this = this;
52919         this.intervalID =  window.setInterval(function() {
52920               _this.check(false);
52921             }, 120000); // every 120 secs = 2mins..
52922         
52923         
52924     },
52925          
52926     
52927     switchLang : function (lang) 
52928     {
52929         _T = typeof(_T) == 'undefined' ? false : _T;
52930           if (!_T || !lang.length) {
52931             return;
52932         }
52933         
52934         if (!_T && lang != 'en') {
52935             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52936             return;
52937         }
52938         
52939         if (typeof(_T.en) == 'undefined') {
52940             _T.en = {};
52941             Roo.apply(_T.en, _T);
52942         }
52943         
52944         if (typeof(_T[lang]) == 'undefined') {
52945             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52946             return;
52947         }
52948         
52949         
52950         Roo.apply(_T, _T[lang]);
52951         // just need to set the text values for everything...
52952         var _this = this;
52953         /* this will not work ...
52954         if (this.form) { 
52955             
52956                
52957             function formLabel(name, val) {
52958                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
52959             }
52960             
52961             formLabel('password', "Password"+':');
52962             formLabel('username', "Email Address"+':');
52963             formLabel('lang', "Language"+':');
52964             this.dialog.setTitle("Login");
52965             this.dialog.buttons[0].setText("Forgot Password");
52966             this.dialog.buttons[1].setText("Login");
52967         }
52968         */
52969         
52970         
52971     },
52972     
52973     
52974     title: "Login",
52975     modal: true,
52976     width:  350,
52977     //height: 230,
52978     height: 180,
52979     shadow: true,
52980     minWidth:200,
52981     minHeight:180,
52982     //proxyDrag: true,
52983     closable: false,
52984     draggable: false,
52985     collapsible: false,
52986     resizable: false,
52987     center: {  // needed??
52988         autoScroll:false,
52989         titlebar: false,
52990        // tabPosition: 'top',
52991         hideTabs: true,
52992         closeOnTab: true,
52993         alwaysShowTabs: false
52994     } ,
52995     listeners : {
52996         
52997         show  : function(dlg)
52998         {
52999             //console.log(this);
53000             this.form = this.layout.getRegion('center').activePanel.form;
53001             this.form.dialog = dlg;
53002             this.buttons[0].form = this.form;
53003             this.buttons[0].dialog = dlg;
53004             this.buttons[1].form = this.form;
53005             this.buttons[1].dialog = dlg;
53006            
53007            //this.resizeToLogo.defer(1000,this);
53008             // this is all related to resizing for logos..
53009             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
53010            //// if (!sz) {
53011              //   this.resizeToLogo.defer(1000,this);
53012              //   return;
53013            // }
53014             //var w = Ext.lib.Dom.getViewWidth() - 100;
53015             //var h = Ext.lib.Dom.getViewHeight() - 100;
53016             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
53017             //this.center();
53018             if (this.disabled) {
53019                 this.hide();
53020                 return;
53021             }
53022             
53023             if (this.user.id < 0) { // used for inital setup situations.
53024                 return;
53025             }
53026             
53027             if (this.intervalID) {
53028                 // remove the timer
53029                 window.clearInterval(this.intervalID);
53030                 this.intervalID = false;
53031             }
53032             
53033             
53034             if (Roo.get('loading')) {
53035                 Roo.get('loading').remove();
53036             }
53037             if (Roo.get('loading-mask')) {
53038                 Roo.get('loading-mask').hide();
53039             }
53040             
53041             //incomming._node = tnode;
53042             this.form.reset();
53043             //this.dialog.modal = !modal;
53044             //this.dialog.show();
53045             this.el.unmask(); 
53046             
53047             
53048             this.form.setValues({
53049                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
53050                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
53051             });
53052             
53053             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
53054             if (this.form.findField('username').getValue().length > 0 ){
53055                 this.form.findField('password').focus();
53056             } else {
53057                this.form.findField('username').focus();
53058             }
53059     
53060         }
53061     },
53062     items : [
53063          {
53064        
53065             xtype : 'ContentPanel',
53066             xns : Roo,
53067             region: 'center',
53068             fitToFrame : true,
53069             
53070             items : [
53071     
53072                 {
53073                
53074                     xtype : 'Form',
53075                     xns : Roo.form,
53076                     labelWidth: 100,
53077                     style : 'margin: 10px;',
53078                     
53079                     listeners : {
53080                         actionfailed : function(f, act) {
53081                             // form can return { errors: .... }
53082                                 
53083                             //act.result.errors // invalid form element list...
53084                             //act.result.errorMsg// invalid form element list...
53085                             
53086                             this.dialog.el.unmask();
53087                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
53088                                         "Login failed - communication error - try again.");
53089                                       
53090                         },
53091                         actioncomplete: function(re, act) {
53092                              
53093                             Roo.state.Manager.set(
53094                                 this.dialog.realm + '.username',  
53095                                     this.findField('username').getValue()
53096                             );
53097                             Roo.state.Manager.set(
53098                                 this.dialog.realm + '.lang',  
53099                                 this.findField('lang').getValue() 
53100                             );
53101                             
53102                             this.dialog.fillAuth(act.result.data);
53103                               
53104                             this.dialog.hide();
53105                             
53106                             if (Roo.get('loading-mask')) {
53107                                 Roo.get('loading-mask').show();
53108                             }
53109                             Roo.XComponent.build();
53110                             
53111                              
53112                             
53113                         }
53114                     },
53115                     items : [
53116                         {
53117                             xtype : 'TextField',
53118                             xns : Roo.form,
53119                             fieldLabel: "Email Address",
53120                             name: 'username',
53121                             width:200,
53122                             autoCreate : {tag: "input", type: "text", size: "20"}
53123                         },
53124                         {
53125                             xtype : 'TextField',
53126                             xns : Roo.form,
53127                             fieldLabel: "Password",
53128                             inputType: 'password',
53129                             name: 'password',
53130                             width:200,
53131                             autoCreate : {tag: "input", type: "text", size: "20"},
53132                             listeners : {
53133                                 specialkey : function(e,ev) {
53134                                     if (ev.keyCode == 13) {
53135                                         this.form.dialog.el.mask("Logging in");
53136                                         this.form.doAction('submit', {
53137                                             url: this.form.dialog.url,
53138                                             method: this.form.dialog.method
53139                                         });
53140                                     }
53141                                 }
53142                             }  
53143                         },
53144                         {
53145                             xtype : 'ComboBox',
53146                             xns : Roo.form,
53147                             fieldLabel: "Language",
53148                             name : 'langdisp',
53149                             store: {
53150                                 xtype : 'SimpleStore',
53151                                 fields: ['lang', 'ldisp'],
53152                                 data : [
53153                                     [ 'en', 'English' ],
53154                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
53155                                     [ 'zh_CN', '\u7C21\u4E2D' ]
53156                                 ]
53157                             },
53158                             
53159                             valueField : 'lang',
53160                             hiddenName:  'lang',
53161                             width: 200,
53162                             displayField:'ldisp',
53163                             typeAhead: false,
53164                             editable: false,
53165                             mode: 'local',
53166                             triggerAction: 'all',
53167                             emptyText:'Select a Language...',
53168                             selectOnFocus:true,
53169                             listeners : {
53170                                 select :  function(cb, rec, ix) {
53171                                     this.form.switchLang(rec.data.lang);
53172                                 }
53173                             }
53174                         
53175                         }
53176                     ]
53177                 }
53178                   
53179                 
53180             ]
53181         }
53182     ],
53183     buttons : [
53184         {
53185             xtype : 'Button',
53186             xns : 'Roo',
53187             text : "Forgot Password",
53188             listeners : {
53189                 click : function() {
53190                     //console.log(this);
53191                     var n = this.form.findField('username').getValue();
53192                     if (!n.length) {
53193                         Roo.MessageBox.alert("Error", "Fill in your email address");
53194                         return;
53195                     }
53196                     Roo.Ajax.request({
53197                         url: this.dialog.url,
53198                         params: {
53199                             passwordRequest: n
53200                         },
53201                         method: this.dialog.method,
53202                         success:  function(response, opts)  {  // check successfull...
53203                         
53204                             var res = this.dialog.processResponse(response);
53205                             if (!res.success) { // error!
53206                                Roo.MessageBox.alert("Error" ,
53207                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
53208                                return;
53209                             }
53210                             Roo.MessageBox.alert("Notice" ,
53211                                 "Please check you email for the Password Reset message");
53212                         },
53213                         failure : function() {
53214                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
53215                         }
53216                         
53217                     });
53218                 }
53219             }
53220         },
53221         {
53222             xtype : 'Button',
53223             xns : 'Roo',
53224             text : "Login",
53225             listeners : {
53226                 
53227                 click : function () {
53228                         
53229                     this.dialog.el.mask("Logging in");
53230                     this.form.doAction('submit', {
53231                             url: this.dialog.url,
53232                             method: this.dialog.method
53233                     });
53234                 }
53235             }
53236         }
53237     ]
53238   
53239   
53240 })
53241  
53242
53243
53244